using System.Reflection;
using Newtonsoft.Json;
using Nop.Core;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Discounts;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Services.Tax;
using Nop.Web.Extensions;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Security;
using Nop.Web.Models.Checkout;
using Nop.Web.Models.Common;
using Nop.Web.Models.ShoppingCart;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Nop.Services.Configuration;
using Telerik.Web.Mvc.UI;
using Nop.Core.Domain.Catalog;
using Nop.Web.Models.Catalog;
using Nop.Web.Models;
using IOrderProcessingService = Nop.Services.Orders.IOrderProcessingService;
using Nop.Services.Messages;

namespace Nop.Web.Controllers
{
    [NopHttpsRequirement(SslRequirement.Yes)]
    public partial class CheckoutController : BaseNopController
    {
        #region Fields

        private readonly IWorkContext _workContext;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly ILocalizationService _localizationService;
        private readonly ITaxService _taxService;
        private readonly ICurrencyService _currencyService;
        private readonly IPriceFormatter _priceFormatter;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly ICustomerService _customerService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly IShippingService _shippingService;
        private readonly IPaymentService _paymentService;
        private readonly IOrderTotalCalculationService _orderTotalCalculationService;
        private readonly ILogger _logger;
        private readonly IOrderService _orderService;
        private readonly IWebHelper _webHelper;
        private readonly HttpContextBase _httpContext;
        private readonly IMobileDeviceHelper _mobileDeviceHelper;
        private readonly IStoreService _storeService;
        private readonly IAddressService _addressService;
        private readonly IProductService _productService;
        private readonly OrderSettings _orderSettings;
        private readonly RewardPointsSettings _rewardPointsSettings;
        private readonly PaymentSettings _paymentSettings;
        private readonly AddressSettings _addressSettings;
        private readonly TaxSettings _taxSettings;
        private readonly IShipingDayService _shipingDayService;
        private readonly Nop.Services.Logistics.ILogisticsService _logisticsService;
        private readonly ICheckoutAttributeService _checkoutAttributeService;
        private readonly ISettingService _settingService;
        private readonly ICheckoutAttributeParser _checkoutAttributeParser;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly IInvoiceService _invoiceService;
        #endregion

        #region Constructors

        public CheckoutController(IWorkContext workContext,
            IShoppingCartService shoppingCartService, ILocalizationService localizationService,
            ITaxService taxService, ICurrencyService currencyService,
            IPriceFormatter priceFormatter, IOrderProcessingService orderProcessingService,
            ICustomerService customerService, IGenericAttributeService genericAttributeService,
            ICountryService countryService, ICheckoutAttributeService checkoutAttributeService,
            IStateProvinceService stateProvinceService, IShippingService shippingService,
            IPaymentService paymentService, IOrderTotalCalculationService orderTotalCalculationService,
            IStoreService storeService, IAddressService addressService,
            ILogger logger, IOrderService orderService, IWebHelper webHelper,
            HttpContextBase httpContext, IMobileDeviceHelper mobileDeviceHelper,
            OrderSettings orderSettings, RewardPointsSettings rewardPointsSettings,
            PaymentSettings paymentSettings, AddressSettings addressSettings, TaxSettings taxSettings, ISettingService settingService,
            IProductService productService, IShipingDayService shipingDayService, Nop.Services.Logistics.ILogisticsService logisticsService,
            ICheckoutAttributeParser checkoutAttributeParser, IWorkflowMessageService workflowMessageService,
            IInvoiceService invoiceService)
        {
            this._workContext = workContext;
            this._shoppingCartService = shoppingCartService;
            this._localizationService = localizationService;
            this._taxService = taxService;
            this._currencyService = currencyService;
            this._priceFormatter = priceFormatter;
            this._orderProcessingService = orderProcessingService;
            this._customerService = customerService;
            this._genericAttributeService = genericAttributeService;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._shippingService = shippingService;
            this._paymentService = paymentService;
            this._orderTotalCalculationService = orderTotalCalculationService;
            this._logger = logger;
            this._orderService = orderService;
            this._webHelper = webHelper;
            this._httpContext = httpContext;
            this._mobileDeviceHelper = mobileDeviceHelper;
            this._storeService = storeService;
            this._addressService = addressService;
            this._productService = productService;
            this._orderSettings = orderSettings;
            this._rewardPointsSettings = rewardPointsSettings;
            this._paymentSettings = paymentSettings;
            this._addressSettings = addressSettings;
            this._taxSettings = taxSettings;
            this._settingService = settingService;
            this._checkoutAttributeService = checkoutAttributeService;
            this._shipingDayService = shipingDayService;
            this._logisticsService = logisticsService;
            this._checkoutAttributeParser = checkoutAttributeParser;
            this._workflowMessageService = workflowMessageService;
            this._invoiceService = invoiceService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected List<ShoppingCartItem> GetCart(int storeId)
        {
            return _workContext.CurrentCustomer.ShoppingCartItems
                .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart)
                .ToList();
        }

        [NonAction]
        protected List<ShoppingCartItem> GetCart()
        {
            var cart = GetCart(GetStoreId());
            return cart;
        }

        [NonAction]
        protected int GetStoreId(int orderId = 0)
        {
            if (orderId != 0)
            {
                var orderSotreId = _orderService.GetOrderById(orderId).StoreId;
                if (orderSotreId != null)
                    return orderSotreId.Value;
            }

            return 0;
        }

        [NonAction]
        protected bool IsPaymentWorkflowRequired(IList<ShoppingCartItem> cart, bool ignoreRewardPoints = false)
        {
            bool result = true;

            //check whether order total equals zero
            decimal? shoppingCartTotalBase = _orderTotalCalculationService.GetShoppingCartTotal(cart, ignoreRewardPoints);
            if (shoppingCartTotalBase.HasValue && shoppingCartTotalBase.Value == decimal.Zero)
                result = false;
            return result;
        }

        [NonAction]
        protected CheckoutAddressModel PrepareCheckoutAddressModel(CheckoutAddressModel model = null, bool isShipping = true, int? addressId = null, int? storeId = null, int? orderId = null)
        {
            if (model == null)
                model = new CheckoutAddressModel();

            var order = new Order();
            if (orderId.HasValue && orderId > 0)
            {
                model.OrderId = orderId.Value;
                order = _orderService.GetOrderById(orderId.Value);
                model.BillingAddressId = order.BillingAddressId;
                model.ShippingAddressId = order.ShippingAddressId.Value;
                var addrm = new AddressModel();
                if (isShipping)
                    addrm.PrepareModel(order.ShippingAddress, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountriesForShipping());
                else
                    addrm.PrepareModel(order.BillingAddress, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountriesForBilling());
                model.Address = addrm;
            }
            else
            {
                if (addressId != null)
                {
                    var addr = _workContext.CurrentCustomer.Addresses.FirstOrDefault(a => a.Id == addressId.Value);
                    if (addr != null)
                    {
                        var addrm = new AddressModel();
                        if (isShipping)
                            addrm.PrepareModel(addr, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountriesForShipping());
                        else
                            addrm.PrepareModel(addr, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountriesForBilling());
                        model.Address = addrm;
                    }
                }

                if (_workContext.CurrentCustomer.BillingAddress != null)
                {
                    model.BillingAddressId = _workContext.CurrentCustomer.BillingAddress.Id;
                    if (!isShipping)
                    {
                        var addrm = new AddressModel();
                        addrm.PrepareModel(_workContext.CurrentCustomer.BillingAddress, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountries());
                        model.Address = addrm;
                    }
                }
                if (_workContext.CurrentCustomer.ShippingAddress != null)
                {
                    model.ShippingAddressId = _workContext.CurrentCustomer.ShippingAddress.Id;
                    if (isShipping)
                    {
                        var addrm = new AddressModel();
                        addrm.PrepareModel(_workContext.CurrentCustomer.ShippingAddress, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountries());
                        model.Address = addrm;
                    }
                }
            }
            if (model.Address == null)
            {
                var addrm = new AddressModel();
                addrm.PrepareModel(null, false, _addressSettings, _localizationService, _stateProvinceService, () => _countryService.GetAllCountries());
                model.Address = addrm;
            }

            var addresses = new List<Address>();
            if (isShipping)
            {
                model.IsShipping = true;
                addresses = _workContext.CurrentCustomer.Addresses.Where(a => a.Country == null || a.Country.AllowsShipping).ToList();
            }
            else
            {
                model.IsShipping = false;
                addresses = _workContext.CurrentCustomer.Addresses.Where(a => a.Country == null || a.Country.AllowsBilling).ToList();
            }
            foreach (var address in addresses)
            {
                var addressModel = new AddressModel();
                addressModel.PrepareModel(address,
                    false,
                    _addressSettings);
                model.AvailableAddresses.Add(addressModel);
            }
            if (!orderId.HasValue || !(orderId > 0))
            {
                var cart = new List<ShoppingCartItem>();
                if (storeId.HasValue)
                {
                    cart = GetCart(storeId.Value);
                }
                else
                {
                    cart = GetCart();
                }
                model.IsPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart, true);
            }
            // Shipment Day
            var shipmentDate = "";
            var shipmentSpan = "";
            if (orderId.HasValue && orderId > 0)
            {
                var addressTypeId = order.ShippingAddress.TypeId;
                var picker = GetPickerModel(order.ShippingAddress.Type.Value, order.ShippingAddress.ZipPostalCode, order.ShipmentDate);
                if (picker.DaysList.Where(d => d.Time == order.ShipmentDate && d.ShipmentTimeSpans.Where(sts => sts.Id == order.ShippingTimeWindowsId).Count() > 0).Count() > 0)
                {
                    model.DatePickerTime = order.ShipmentDate.HasValue ? order.ShipmentDate.Value.Date.ToString("yyyy/MM/dd") : null;
                    model.DatePickerTimeSpan = order.ShippingTimeWindowsId.HasValue ? order.ShippingTimeWindowsId.ToString() : null;
                    model.HasScheduledDate = true;
                    if (picker.FirstAvailableDate == order.ShipmentDate && picker.FirstAvailableTimeWindow == order.ShippingTimeWindowsId)
                    {
                        model.HasScheduledDate = false;
                    }
                }
                else
                {
                    model.DatePickerTime = picker.FirstAvailableDate.ToString();
                    model.DatePickerTimeSpan = picker.FirstAvailableTimeWindow.ToString();
                    model.HasScheduledDate = false;
                }
                model.DatePicker = picker;
            }
            else if (_workContext.CurrentCustomer.ShippingAddress != null && _workContext.CurrentCustomer.ShippingAddress.Type != null)
            {
                DateTime? inputDate = null;
                GetScheduledDateTime(out shipmentDate, out shipmentSpan);
                model.HasScheduledDate = !String.IsNullOrEmpty(shipmentDate) && !String.IsNullOrEmpty(shipmentSpan);
                if (model.HasScheduledDate)
                {
                    model.DatePickerTime = shipmentDate;
                    model.DatePickerTimeSpan = shipmentSpan;
                    DateTime InputDate = new DateTime();
                    DateTime.TryParse(shipmentDate, out InputDate);
                    if (InputDate.Year >= DateTime.UtcNow.Year)
                    {
                        inputDate = InputDate;
                    }
                }
                var addressTypeId = _workContext.CurrentCustomer.ShippingAddress.Type.Value;
                model.DatePicker = GetPickerModel(addressTypeId, _workContext.CurrentCustomer.ShippingAddress.ZipPostalCode, inputDate);
            }
            return model;
        }

        //[NonAction]
        //protected CheckoutBillingAddressModel PrepareBillingAddressModel(int? selectedCountryId = null)
        //{
        //    var model = new CheckoutBillingAddressModel();
        //    //existing addresses
        //    var addresses = _workContext.CurrentCustomer.Addresses.Where(a => a.Country == null || a.Country.AllowsBilling).ToList();
        //    foreach (var address in addresses)
        //    {
        //        var addressModel = new AddressModel();
        //        addressModel.PrepareModel(address,
        //            false,
        //            _addressSettings);
        //        model.ExistingAddresses.Add(addressModel);
        //    }

        //    //new address
        //    model.NewAddress.CountryId = selectedCountryId;
        //    model.NewAddress.PrepareModel(null,
        //        false,
        //        _addressSettings,
        //        _localizationService,
        //        _stateProvinceService,
        //        () => _countryService.GetAllCountriesForBilling());
        //    return model;
        //}

        //[NonAction]
        //protected CheckoutShippingAddressModel PrepareShippingAddressModel(int? selectedCountryId = null)
        //{
        //    var model = new CheckoutShippingAddressModel();
        //    //existing addresses
        //    var addresses = _workContext.CurrentCustomer.Addresses.Where(a => a.Country == null || a.Country.AllowsShipping).ToList();
        //    foreach (var address in addresses)
        //    {
        //        var addressModel = new AddressModel();
        //        addressModel.PrepareModel(address,
        //            false,
        //            _addressSettings);
        //        model.ExistingAddresses.Add(addressModel);
        //    }

        //    //new address
        //    model.NewAddress.CountryId = selectedCountryId;
        //    model.NewAddress.PrepareModel(null,
        //        false,
        //        _addressSettings,
        //        _localizationService,
        //        _stateProvinceService,
        //        () => _countryService.GetAllCountriesForShipping());
        //    return model;
        //}

        [NonAction]
        protected CheckoutShippingMethodModel PrepareShippingMethodModel(IList<ShoppingCartItem> cart)
        {
            var model = new CheckoutShippingMethodModel();

            var getShippingOptionResponse = _shippingService.GetShippingOptions(cart, _workContext.CurrentCustomer.ShippingAddress, "", true);
            if (getShippingOptionResponse.Success)
            {
                //performance optimization. cache returned shipping options.
                //we'll use them later (after a customer has selected an option).
                _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.OfferedShippingOptions, getShippingOptionResponse.ShippingOptions);

                foreach (var shippingOption in getShippingOptionResponse.ShippingOptions)
                {
                    var soModel = new CheckoutShippingMethodModel.ShippingMethodModel()
                    {
                        Name = shippingOption.Name,
                        Description = shippingOption.Description,
                        ShippingRateComputationMethodSystemName = shippingOption.ShippingRateComputationMethodSystemName,
                    };

                    //adjust rate
                    Discount appliedDiscount = null;
                    var shippingTotal = _orderTotalCalculationService.AdjustShippingRate(
                        shippingOption.Rate, cart, out appliedDiscount);

                    decimal rateBase = _taxService.GetShippingPrice(shippingTotal, _workContext.CurrentCustomer);
                    decimal rate = _currencyService.ConvertFromPrimaryStoreCurrency(rateBase, _workContext.WorkingCurrency);
                    soModel.Fee = _priceFormatter.FormatShippingPrice(rate, true);

                    model.ShippingMethods.Add(soModel);
                }

                //find a selected (previously) shipping method
                var lastShippingOption = _workContext.CurrentCustomer.GetAttribute<ShippingOption>(SystemCustomerAttributeNames.LastShippingOption);
                if (lastShippingOption != null)
                {
                    var shippingOptionToSelect = model.ShippingMethods.ToList()
                        .Find(so => !String.IsNullOrEmpty(so.Name) && so.Name.Equals(lastShippingOption.Name, StringComparison.InvariantCultureIgnoreCase) &&
                        !String.IsNullOrEmpty(so.ShippingRateComputationMethodSystemName) && so.ShippingRateComputationMethodSystemName.Equals(lastShippingOption.ShippingRateComputationMethodSystemName, StringComparison.InvariantCultureIgnoreCase));
                    if (shippingOptionToSelect != null)
                        shippingOptionToSelect.Selected = true;
                }
                //if no option has been selected, let's do it for the first one
                if (model.ShippingMethods.Where(so => so.Selected).FirstOrDefault() == null)
                {
                    var shippingOptionToSelect = model.ShippingMethods.FirstOrDefault();
                    if (shippingOptionToSelect != null)
                        shippingOptionToSelect.Selected = true;
                }
            }
            else
                foreach (var error in getShippingOptionResponse.Errors)
                {
                    model.Warnings.Add(error);
                    ErrorNotification(error);
                }

            return model;
        }

        [NonAction]
        protected CheckoutPaymentMethodModel PreparePaymentMethodModel(IList<ShoppingCartItem> cart)
        {
            var model = new CheckoutPaymentMethodModel();

            //reward points
            if (_rewardPointsSettings.Enabled && cart != null && !cart.IsRecurring())
            {
                int rewardPointsBalance = _workContext.CurrentCustomer.GetRewardPointsBalance();
                decimal rewardPointsAmountBase = _orderTotalCalculationService.ConvertRewardPointsToAmount(rewardPointsBalance);
                decimal rewardPointsAmount = _currencyService.ConvertFromPrimaryStoreCurrency(rewardPointsAmountBase, _workContext.WorkingCurrency);
                if (rewardPointsAmount > decimal.Zero)
                {
                    model.DisplayRewardPoints = true;
                    model.RewardPointsAmount = _priceFormatter.FormatPrice(rewardPointsAmount, true, false);
                    model.RewardPointsBalance = rewardPointsBalance;
                }
            }

            var boundPaymentMethods = _paymentService
                .LoadActivePaymentMethods(_workContext.CurrentCustomer.Id)
                .Where(pm =>
                    pm.PaymentMethodType == PaymentMethodType.Standard ||
                    pm.PaymentMethodType == PaymentMethodType.Redirection)
                .ToList();
            foreach (var pm in boundPaymentMethods)
            {
                if (cart != null && cart.IsRecurring() && pm.RecurringPaymentType == RecurringPaymentType.NotSupported)
                    continue;

                var pmModel = new CheckoutPaymentMethodModel.PaymentMethodModel()
                {
                    Name = pm.GetLocalizedFriendlyName(_localizationService, _workContext.WorkingLanguage.Id),
                    PaymentMethodSystemName = pm.PluginDescriptor.SystemName,
                };
                //payment method additional fee
                decimal paymentMethodAdditionalFee = decimal.Zero;
                if (cart != null)
                    paymentMethodAdditionalFee = _paymentService.GetAdditionalHandlingFee(cart, pm.PluginDescriptor.SystemName);
                decimal rateBase = _taxService.GetPaymentMethodAdditionalFee(paymentMethodAdditionalFee, _workContext.CurrentCustomer);
                decimal rate = _currencyService.ConvertFromPrimaryStoreCurrency(rateBase, _workContext.WorkingCurrency);
                if (rate > decimal.Zero)
                    pmModel.Fee = _priceFormatter.FormatPaymentMethodAdditionalFee(rate, true);

                model.PaymentMethods.Add(pmModel);
            }

            //find a selected (previously) payment method
            if (!String.IsNullOrEmpty(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName))
            {
                var paymentMethodToSelect = model.PaymentMethods.ToList()
                    .Find(pm => pm.PaymentMethodSystemName.Equals(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName, StringComparison.InvariantCultureIgnoreCase));
                if (paymentMethodToSelect != null)
                    paymentMethodToSelect.Selected = true;
            }
            //if no option has been selected, let's do it for the first one
            if (model.PaymentMethods.Where(so => so.Selected).FirstOrDefault() == null)
            {
                var paymentMethodToSelect = model.PaymentMethods.FirstOrDefault();
                if (paymentMethodToSelect != null)
                    paymentMethodToSelect.Selected = true;
            }

            return model;
        }

        [NonAction]
        protected CheckoutPaymentInfoModel PreparePaymentInfoModel(IPaymentMethod paymentMethod, int? storeId = null)
        {
            var model = new CheckoutPaymentInfoModel();
            string actionName;
            string controllerName;
            RouteValueDictionary routeValues;
            //var sid = storeId.HasValue ? storeId.Value : GetStoreId();

            //to-do:store id if is null,set default value 31
            var sid = storeId.HasValue ? storeId.Value : 31;
			
            paymentMethod.GetPaymentInfoRoute(out actionName, out controllerName, out routeValues);
            model.PaymentInfoActionName = actionName;
            model.PaymentInfoControllerName = controllerName;
            model.PaymentInfoRouteValues = routeValues;
            model.ShowSubmitButton = true;
            model.PaymentInfoRouteValues.Add("customerId", _workContext.CurrentCustomer.Id);
            model.PaymentInfoRouteValues.Add("storeId", sid);
            model.StoreId = sid;
            model.PaymentInfoRouteValues.Add("returnRouteName", "CheckoutOpcSavePaymentInfo");
            model.PaymentInfoRouteValues.Add("type", "commerce");
            model.DisplayOrderTotals = _orderSettings.OnePageCheckoutDisplayOrderTotalsOnPaymentInfoTab;
            model.HasAddressList = _workContext.CurrentCustomer.Addresses != null && _workContext.CurrentCustomer.Addresses.Count > 0;
            if (_workContext.CurrentCustomer.BillingAddress != null)
            {
                model.BillingAddressId = _workContext.CurrentCustomer.BillingAddress.Id;
                var billingAddress = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == _workContext.CurrentCustomer.BillingAddress.Id).FirstOrDefault();
                var billingAddressModel = new AddressModel();
                billingAddressModel.PrepareModel(billingAddress,
                    false,
                    _addressSettings);
                model.BillingAddress = billingAddressModel;
            }
            return model;
        }

        [NonAction]
        protected CheckoutConfirmModel PrepareConfirmOrderModel(IList<ShoppingCartItem> cart = null, Order order = null)
        {
            var model = new CheckoutConfirmModel();
            if (cart != null)
            {
                //min order amount validation
                bool minOrderTotalAmountOk = _orderProcessingService.ValidateMinOrderTotalAmount(cart);
                if (!minOrderTotalAmountOk)
                {
                    decimal minOrderTotalAmount = _currencyService.ConvertFromPrimaryStoreCurrency(_orderSettings.MinOrderTotalAmount, _workContext.WorkingCurrency);
                    model.MinOrderTotalWarning = string.Format(_localizationService.GetResource("Checkout.MinOrderTotalAmount"), _priceFormatter.FormatPrice(minOrderTotalAmount, true, false));
                    ErrorNotification(string.Format(_localizationService.GetResource("Checkout.MinOrderTotalAmount"), _priceFormatter.FormatPrice(minOrderTotalAmount, true, false)));
                }
                if (_workContext.CurrentCustomer.BillingAddress != null)
                {
                    int billingAddressId = _workContext.CurrentCustomer.BillingAddress.Id;
                    var billingAddress = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == billingAddressId).FirstOrDefault();
                    var billingAddressModel = new AddressModel();
                    billingAddressModel.PrepareModel(billingAddress,
                        false,
                        _addressSettings);
                    model.BillingAddress = billingAddressModel;
                }
                if (_workContext.CurrentCustomer.ShippingAddress != null)
                {
                    int shippingAddressId = _workContext.CurrentCustomer.ShippingAddress.Id;
                    var shippingAddress = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == shippingAddressId).FirstOrDefault();
                    var shippingAddressModel = new AddressModel();
                    shippingAddressModel.PrepareModel(shippingAddress,
                        false,
                        _addressSettings);
                    model.ShippingAddress = shippingAddressModel;
                }

                model.ShowPaymentInfo = false;
                model.CanEditPaymentMethods = false;
                var shippingOption = _workContext.CurrentCustomer.GetAttribute<ShippingOption>(SystemCustomerAttributeNames.LastShippingOption);
                if (shippingOption != null)
                {
                    model.ShippingRateComputationMethodSystemName = _shippingService.LoadShippingRateComputationMethodBySystemName(shippingOption.ShippingRateComputationMethodSystemName).GetLocalizedFriendlyName(_localizationService, _workContext.WorkingLanguage.Id);
                }
                var processPaymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest;
                if (processPaymentRequest != null && !String.IsNullOrEmpty(processPaymentRequest.CreditCardNumber))
                {
                    model.ShowPaymentInfo = true;
                    model.CreditCardName = processPaymentRequest.CreditCardName;
                    model.CreditCardNumber = processPaymentRequest.CreditCardNumber;
                    model.CreditCardType = processPaymentRequest.CreditCardType;
                    model.CreditCardExpireMonth = processPaymentRequest.CreditCardExpireMonth;
                    model.CreditCardExpireYear = processPaymentRequest.CreditCardExpireYear;
                }

                if (model.ShowPaymentInfo)
                {
                    var paymentMethodModel = PreparePaymentMethodModel(cart);
                    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne && paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                    {
                        model.CanEditPaymentMethods = false;
                    }
                    else
                    {
                        model.CanEditPaymentMethods = true;
                    }
                }

                string shipmentDate = "";
                string shipmentSpan = "";
                GetScheduledDateTime(out shipmentDate, out shipmentSpan);
                if (!String.IsNullOrEmpty(shipmentDate) && !String.IsNullOrEmpty(shipmentSpan))
                {
                    var timeWindow = _logisticsService.GetAllShippingTimeWindows().FirstOrDefault(stw => stw.Id.ToString() == shipmentSpan);
                    if (timeWindow != null)
                    {
                        var shipmentSpanDesc = timeWindow.Description;
                        model.EstimatedDeliveryTime = shipmentDate + " " + shipmentSpanDesc;
                    }
                }
                else
                {
                    var day = _logisticsService.GetValidateDays((int)OrderKind.Commerce, (AddressType)(model.ShippingAddress.TypeId.Value), model.ShippingAddress.ZipPostalCode).FirstOrDefault();
                    model.EstimatedDeliveryTime = day.Time.ToString("yyyy/MM/dd") + " " + day.ShipmentTimeSpans[0].Description;
                }

                //gift message
                var checkoutAttribute = _genericAttributeService.GetAttribute(_workContext.CurrentCustomer.Id, "customer",
                      SystemCustomerAttributeNames.CheckoutAttributes);
                if (checkoutAttribute != null && !string.IsNullOrEmpty(checkoutAttribute.Value))
                {
                    model.CheckoutAttributeViewModel = _checkoutAttributeParser.ParseCheckoutAttribute(checkoutAttribute.Value);
                }
            }
            else if (order != null)
            {
                model.OrderId = order.Id;
                model.Order = order;
                model.BillingAddress = new AddressModel();
                model.ShippingAddress = new AddressModel();
                model.BillingAddress.PrepareModel(order.BillingAddress, false, _addressSettings);
                model.ShippingAddress.PrepareModel(order.ShippingAddress, false, _addressSettings);
                model.ShowPaymentInfo = false;
                model.CanEditPaymentMethods = false;
                model.ShippingRateComputationMethodSystemName = order.ShippingRateComputationMethodSystemName;

                string shipmentDate = order.ShipmentDate.ToString();
                string shipmentSpan = order.ShippingTimeWindowsId.ToString();
                if (!String.IsNullOrEmpty(shipmentDate) && !String.IsNullOrEmpty(shipmentSpan))
                {
                    var timeWindow = _logisticsService.GetAllShippingTimeWindows().FirstOrDefault(stw => stw.Id.ToString() == shipmentSpan);
                    if (timeWindow != null)
                    {
                        var shipmentSpanDesc = timeWindow.Description;
                        model.EstimatedDeliveryTime = shipmentDate + " " + shipmentSpanDesc;
                    }
                }
                else
                {
                    var day = _logisticsService.GetValidateDays((int)OrderKind.Commerce, (AddressType)(model.ShippingAddress.TypeId.Value), model.ShippingAddress.ZipPostalCode).FirstOrDefault();
                    model.EstimatedDeliveryTime = day.Time.ToString("yyyy/MM/dd") + " " + day.ShipmentTimeSpans[0].Description;
                }

                //gift message
                if (order.CheckoutAttributeDescription != null && !string.IsNullOrEmpty(order.CheckoutAttributeDescription))
                {
                    model.CheckoutAttributeViewModel = _checkoutAttributeParser.ParseCheckoutAttribute(order.CheckoutAttributeDescription);
                }

            }
            return model;
        }

        [NonAction]
        protected bool UseOnePageCheckout()
        {
            bool useMobileDevice = _mobileDeviceHelper.IsMobileDevice(_httpContext)
                && _mobileDeviceHelper.MobileDevicesSupported()
                && !_mobileDeviceHelper.CustomerDontUseMobileVersion();

            //mobile version doesn't support one-page checkout
            if (useMobileDevice)
                return false;

            //check the appropriate setting
            return _orderSettings.OnePageCheckoutEnabled;
        }

        [NonAction]
        protected bool IsMinimumOrderPlacementIntervalValid(Customer customer)
        {
            //prevent 2 orders being placed within an X seconds time frame
            if (_orderSettings.MinimumOrderPlacementInterval == 0)
                return true;

            var lastOrderPlaced = _orderService.GetOrdersByCustomerId(_workContext.CurrentCustomer.Id)
                .FirstOrDefault();
            if (lastOrderPlaced == null)
                return true;

            var interval = DateTime.UtcNow - lastOrderPlaced.CreatedOnUtc;
            return interval.TotalSeconds > _orderSettings.MinimumOrderPlacementInterval;
        }

        private void InsertScheduledDateTime(string shipmentDate, string shipmentSpan)
        {
            if (string.IsNullOrEmpty(shipmentDate) || string.IsNullOrEmpty(shipmentSpan)) return;

            var gen = new GenericAttribute()
            {
                EntityId = _workContext.CurrentCustomer.Id,
                KeyGroup = "Customer",
                Key = "ShipmentTime",
                Value = string.Format("{0};{1}", shipmentDate, shipmentSpan)
            };

            _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.ShipmentTime, gen.Value);
        }

        private void GetScheduledDateTime(out string shipmentDate, out string shipmentSpan)
        {
            shipmentDate = "";
            shipmentSpan = "";
            var gen = _genericAttributeService.GetAttribute(_workContext.CurrentCustomer.Id, "Customer", SystemCustomerAttributeNames.ShipmentTime);
            if (gen != null && !String.IsNullOrEmpty(gen.Value))
            {
                char[] delimiterChars = { ';' };
                string[] dateTimeStr = gen.Value.Split(delimiterChars);
                if (dateTimeStr.Length == 2)
                {
                    shipmentDate = dateTimeStr[0];
                    shipmentSpan = dateTimeStr[1];
                }
                else
                {
                    _genericAttributeService.DeleteAttribute(gen);
                }
            }
        }

        private void RemoveScheduledDateTime()
        {
            var gen = _genericAttributeService.GetAttribute(_workContext.CurrentCustomer.Id, "Customer", "ShipmentTime");
            if (gen != null && !String.IsNullOrEmpty(gen.Value))
            {
                _genericAttributeService.DeleteAttribute(gen);
            }
        }

        #endregion

        #region Methods (multistep checkout)

        //public ActionResult Index(int? storeId)
        //{
        //    //validate
        //    if (!storeId.HasValue)
        //        return RedirectToRoute("ShoppingCart");

        //    var cart = GetCart(storeId.Value);
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //reset checkout data
        //    _customerService.ResetCheckoutData(_workContext.CurrentCustomer);

        //    //validation (cart)
        //    var scWarnings = _shoppingCartService.GetShoppingCartWarnings(cart, _workContext.CurrentCustomer.CheckoutAttributes, true);
        //    if (scWarnings.Count > 0)
        //        return RedirectToRoute("ShoppingCart");
        //    //validation (each shopping cart item)
        //    foreach (ShoppingCartItem sci in cart)
        //    {
        //        var sciWarnings = _shoppingCartService.GetShoppingCartItemWarnings(_workContext.CurrentCustomer,
        //            sci.ShoppingCartType,
        //            sci.ProductVariant,
        //            sci.AttributesXml,
        //            sci.CustomerEnteredPrice,
        //            sci.Quantity,
        //            false);
        //        if (sciWarnings.Count > 0)
        //            return RedirectToRoute("ShoppingCart");
        //    }

        //    return RedirectToRoute("CheckoutOnePage");
        //    //if (UseOnePageCheckout())
        //    //    return RedirectToRoute("CheckoutOnePage");
        //    //else
        //    //    return RedirectToRoute("CheckoutAddress");
        //    // return RedirectToRoute("CheckoutBillingAddress");
        //}

        //public ActionResult Address()
        //{
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    var model = PrepareCheckoutAddressModel();
        //    return View(model);
        //}

        //public ActionResult BillingAddress(int? storeId)
        //{
        //    //validation
        //    if (!storeId.HasValue)
        //        return RedirectToRoute("ShoppingCart");

        //    var cart = GetCart(storeId.Value);
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //model
        //    var model = PrepareBillingAddressModel();
        //    return View(model);
        //}
        //public ActionResult SelectBillingAddress(int addressId)
        //{
        //    var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
        //    if (address == null)
        //        return RedirectToRoute("CheckoutBillingAddress");

        //    _workContext.CurrentCustomer.BillingAddress = address;
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    return RedirectToRoute("CheckoutShippingAddress");
        //}
        //public ActionResult UpdateBillingAddress(int addressId)
        //{
        //    if (addressId == 0)
        //    {
        //        _workContext.CurrentCustomer.BillingAddress = null;
        //    }
        //    else
        //    {
        //        var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
        //        if (address == null)
        //            return RedirectToRoute("CheckoutAddress");

        //        _workContext.CurrentCustomer.BillingAddress = address;
        //    }
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    return RedirectToRoute("CheckoutAddress");
        //}
        //[HttpPost, ActionName("BillingAddress")]
        //[FormValueRequired("nextstep")]
        //public ActionResult NewBillingAddress(CheckoutBillingAddressModel model)
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    if (ModelState.IsValid)
        //    {
        //        var address = model.NewAddress.ToEntity();
        //        address.CreatedOnUtc = DateTime.UtcNow;
        //        //some validation
        //        if (address.CountryId == 0)
        //            address.CountryId = null;
        //        if (address.StateProvinceId == 0)
        //            address.StateProvinceId = null;
        //        _workContext.CurrentCustomer.Addresses.Add(address);
        //        _workContext.CurrentCustomer.BillingAddress = address;
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //        return RedirectToRoute("CheckoutAddress");
        //    }


        //    //If we got this far, something failed, redisplay form
        //    model = PrepareBillingAddressModel(model.NewAddress.CountryId);
        //    return View(model);
        //}

        //public ActionResult CheckoutCompleteAddress()
        //{
        //    if (_workContext.CurrentCustomer.BillingAddress != null && _workContext.CurrentCustomer.ShippingAddress != null)
        //    {
        //        return RedirectToRoute("CheckoutShippingMethod");
        //    }
        //    else
        //    {
        //        return RedirectToRoute("CheckoutAddress");
        //    }
        //}

        //public ActionResult ShippingAddress()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    if (!cart.RequiresShipping())
        //    {
        //        _workContext.CurrentCustomer.ShippingAddress = null;
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);
        //        return RedirectToRoute("CheckoutShippingMethod");
        //    }

        //    //model
        //    var model = PrepareShippingAddressModel();
        //    return View(model);
        //}
        //public ActionResult SelectShippingAddress(int addressId)
        //{
        //    var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
        //    if (address == null)
        //        return RedirectToRoute("CheckoutShippingAddress");

        //    _workContext.CurrentCustomer.ShippingAddress = address;
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    return RedirectToRoute("CheckoutShippingMethod");
        //}
        //public ActionResult UpdateShippingAddress(int addressId)
        //{
        //    if (addressId == 0)
        //    {
        //        _workContext.CurrentCustomer.ShippingAddress = null;
        //    }
        //    else
        //    {
        //        var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
        //        if (address == null)
        //            return RedirectToRoute("CheckoutAddress");

        //        _workContext.CurrentCustomer.ShippingAddress = address;
        //    }
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    return RedirectToRoute("CheckoutAddress");
        //}
        //[HttpPost, ActionName("ShippingAddress")]
        //[FormValueRequired("nextstep")]
        //public ActionResult NewShippingAddress(CheckoutShippingAddressModel model)
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    if (!cart.RequiresShipping())
        //    {
        //        _workContext.CurrentCustomer.ShippingAddress = null;
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);
        //        return RedirectToRoute("CheckoutAddress");
        //    }

        //    if (ModelState.IsValid)
        //    {
        //        var address = model.NewAddress.ToEntity();
        //        address.CreatedOnUtc = DateTime.UtcNow;
        //        //some validation
        //        if (address.CountryId == 0)
        //            address.CountryId = null;
        //        if (address.StateProvinceId == 0)
        //            address.StateProvinceId = null;
        //        _workContext.CurrentCustomer.Addresses.Add(address);
        //        _workContext.CurrentCustomer.ShippingAddress = address;
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //        return RedirectToRoute("CheckoutAddress");
        //    }


        //    //If we got this far, something failed, redisplay form
        //    model = PrepareShippingAddressModel(model.NewAddress.CountryId);
        //    return View(model);
        //}


        //public ActionResult ShippingMethod()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    if (!cart.RequiresShipping())
        //    {
        //        _genericAttributeService.SaveAttribute<ShippingOption>(_workContext.CurrentCustomer, SystemCustomerAttributeNames.LastShippingOption, null);
        //        return RedirectToRoute("CheckoutPaymentMethod");
        //    }


        //    //model
        //    var model = PrepareShippingMethodModel(cart);
        //    return View(model);
        //}
        //[HttpPost, ActionName("ShippingMethod")]
        //[FormValueRequired("nextstep")]
        //[ValidateInput(false)]
        //public ActionResult SelectShippingMethod(string shippingoption)
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    if (!cart.RequiresShipping())
        //    {
        //        _genericAttributeService.SaveAttribute<ShippingOption>(_workContext.CurrentCustomer, SystemCustomerAttributeNames.LastShippingOption, null);
        //        return RedirectToRoute("CheckoutPaymentMethod");
        //    }

        //    //parse selected method 
        //    if (String.IsNullOrEmpty(shippingoption))
        //        return ShippingMethod();
        //    var splittedOption = shippingoption.Split(new string[] { "___" }, StringSplitOptions.RemoveEmptyEntries);
        //    if (splittedOption.Length != 2)
        //        return ShippingMethod();
        //    string selectedName = splittedOption[0];
        //    string shippingRateComputationMethodSystemName = splittedOption[1];

        //    //find it
        //    //performance optimization. try cache first
        //    var shippingOptions = _workContext.CurrentCustomer.GetAttribute<List<ShippingOption>>(SystemCustomerAttributeNames.OfferedShippingOptions);
        //    if (shippingOptions == null || shippingOptions.Count == 0)
        //    {
        //        //not found? let's load them using shipping service
        //        shippingOptions = _shippingService
        //            .GetShippingOptions(cart, _workContext.CurrentCustomer.ShippingAddress, shippingRateComputationMethodSystemName)
        //            .ShippingOptions
        //            .ToList();
        //    }
        //    else
        //    {
        //        //loaded cached results. let's filter result by a chosen shipping rate computation method
        //        shippingOptions = shippingOptions.Where(so => so.ShippingRateComputationMethodSystemName.Equals(shippingRateComputationMethodSystemName, StringComparison.InvariantCultureIgnoreCase))
        //            .ToList();
        //    }

        //    var shippingOption = shippingOptions
        //        .Find(so => !String.IsNullOrEmpty(so.Name) && so.Name.Equals(selectedName, StringComparison.InvariantCultureIgnoreCase));
        //    if (shippingOption == null)
        //        return ShippingMethod();

        //    //save
        //    _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.LastShippingOption, shippingOption);

        //    return RedirectToRoute("CheckoutPaymentMethod");
        //}


        //public ActionResult PaymentMethod()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //Check whether payment workflow is required
        //    //we ignore reward points during cart total calculation
        //    bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart, true);
        //    if (!isPaymentWorkflowRequired)
        //    {
        //        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);
        //        return RedirectToRoute("CheckoutPaymentInfo");
        //    }

        //    //model
        //    var paymentMethodModel = PreparePaymentMethodModel(cart);

        //    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
        //        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
        //    {
        //        //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
        //        //so customer doesn't have to choose a payment method
        //        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);
        //        return RedirectToRoute("CheckoutPaymentInfo");
        //    }

        //    return View(paymentMethodModel);
        //}
        //[HttpPost, ActionName("PaymentMethod")]
        //[FormValueRequired("nextstep")]
        //[ValidateInput(false)]
        //public ActionResult SelectPaymentMethod(string paymentmethod, CheckoutPaymentMethodModel model)
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //reward points
        //    _workContext.CurrentCustomer.UseRewardPointsDuringCheckout = model.UseRewardPoints;
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    //Check whether payment workflow is required
        //    bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart);
        //    if (!isPaymentWorkflowRequired)
        //    {
        //        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
        //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);
        //        return RedirectToRoute("CheckoutPaymentInfo");
        //    }
        //    //payment method 
        //    if (String.IsNullOrEmpty(paymentmethod))
        //        return PaymentMethod();

        //    var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(paymentmethod);
        //    if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
        //        return PaymentMethod();

        //    //save
        //    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentmethod;
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    return RedirectToRoute("CheckoutPaymentInfo");
        //}


        //public ActionResult PaymentInfo()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //Check whether payment workflow is required
        //    bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart);
        //    if (!isPaymentWorkflowRequired)
        //    {
        //        return RedirectToRoute("CheckoutConfirm");
        //    }

        //    var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
        //    if (paymentMethod == null)
        //        return RedirectToRoute("CheckoutPaymentMethod");

        //    //model
        //    var model = PreparePaymentInfoModel(paymentMethod);
        //    return View(model);
        //}
        //[HttpPost, ActionName("PaymentInfo")]
        //[FormValueRequired("nextstep")]
        //[ValidateInput(false)]
        //public ActionResult EnterPaymentInfo(FormCollection form)
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //Check whether payment workflow is required
        //    bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart);
        //    if (!isPaymentWorkflowRequired)
        //    {
        //        return RedirectToRoute("CheckoutConfirm");
        //    }

        //    var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
        //    if (paymentMethod == null)
        //        return RedirectToRoute("CheckoutPaymentMethod");

        //    var paymentControllerType = paymentMethod.GetControllerType();
        //    var paymentController = DependencyResolver.Current.GetService(paymentControllerType) as BaseNopPaymentController;
        //    var warnings = paymentController.ValidatePaymentForm(form);
        //    foreach (var warning in warnings)
        //        ModelState.AddModelError("", warning);
        //    if (ModelState.IsValid)
        //    {
        //        //get payment info
        //        var paymentInfo = paymentController.GetPaymentInfo(form);
        //        //session save
        //        _httpContext.Session["OrderPaymentInfo"] = paymentInfo;
        //        return RedirectToRoute("CheckoutConfirm");
        //    }

        //    //If we got this far, something failed, redisplay form
        //    //model
        //    var model = PreparePaymentInfoModel(paymentMethod);
        //    return View(model);
        //}

        //public ActionResult Confirm()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //model
        //    var model = PrepareConfirmOrderModel(cart);
        //    return View(model);
        //}
        //[HttpPost, ActionName("Confirm")]
        //[ValidateInput(false)]
        //public ActionResult ConfirmOrder()
        //{
        //    //validation
        //    var cart = _workContext.CurrentCustomer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
        //    if (cart.Count == 0)
        //        return RedirectToRoute("ShoppingCart");

        //    if (UseOnePageCheckout())
        //        return RedirectToRoute("CheckoutOnePage");

        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();


        //    //model
        //    var model = new CheckoutConfirmModel();

        //    bool checkoutComplete = true;
        //    string successOrderslist = "";
        //    var storeOrder = _storeService.GetStoreShoppingCartItems(cart);

        //    try
        //    {
        //        var processPaymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest;
        //        if (processPaymentRequest == null)
        //        {
        //            //Check whether payment workflow is required
        //            if (IsPaymentWorkflowRequired(cart))
        //                return RedirectToRoute("CheckoutPaymentInfo");
        //            else
        //                processPaymentRequest = new ProcessPaymentRequest();
        //        }

        //        //prevent 2 orders being placed within an X seconds time frame
        //        if (!IsMinimumOrderPlacementIntervalValid(_workContext.CurrentCustomer))
        //            throw new Exception(_localizationService.GetResource("Checkout.MinOrderPlacementInterval"));

        //        //place order
        //        processPaymentRequest.CustomerId = _workContext.CurrentCustomer.Id;
        //        processPaymentRequest.PaymentMethodSystemName = _workContext.CurrentCustomer.SelectedPaymentMethodSystemName;
        //        //var placeOrderResult = _orderProcessingService.PlaceOrder(processPaymentRequest);
        //        //if (placeOrderResult.Success)
        //        //{
        //        //    _httpContext.Session["OrderPaymentInfo"] = null;
        //        //    var postProcessPaymentRequest = new PostProcessPaymentRequest()
        //        //    {
        //        //        Order = placeOrderResult.PlacedOrder
        //        //    };
        //        //    _paymentService.PostProcessPayment(postProcessPaymentRequest);

        //        //    if (_webHelper.IsRequestBeingRedirected || _webHelper.IsPostBeingDone)
        //        //    {
        //        //        //redirection or POST has been done in PostProcessPayment
        //        //        return Content("Redirected");
        //        //    }
        //        //    else
        //        //    {
        //        //        //if no redirection has been done (to a third-party payment page)
        //        //        //theoretically it's not possible
        //        //        return RedirectToRoute("CheckoutCompleted");
        //        //    }
        //        //}
        //        //else
        //        //{
        //        //    foreach (var error in placeOrderResult.Errors)
        //        //        model.Warnings.Add(error);
        //        //}

        //        //TODO: Verify check out process.

        //        storeOrder.ProcessPaymentRequest = processPaymentRequest;
        //        var result = _orderProcessingService.PlaceOrder(storeOrder);

        //        if (result.Success)
        //        {
        //            foreach (var order in result.PlacedOrders)
        //            {
        //                var postProcessPaymentRequest = new PostProcessPaymentRequest()
        //                {
        //                    Order = order
        //                };
        //                _paymentService.PostProcessPayment(postProcessPaymentRequest);
        //            }

        //        }
        //        else
        //        {
        //            foreach (var error in result.Errors)
        //                model.Warnings.Add(error);
        //            checkoutComplete = false;
        //        }

        //        successOrderslist = String.Join(",",
        //            result.PlacedOrders.Select(o => o.Id.ToString()));
        //    }
        //    catch (Exception exc)
        //    {
        //        _logger.Warning(exc.Message, exc);
        //        model.Warnings.Add(exc.Message);
        //    }

        //    if (checkoutComplete)
        //    {
        //        this.Session["OrderPaymentInfo"] = null;
        //        return RedirectToRoute("CheckoutCompleted", new { successOrderList = successOrderslist });
        //    }

        //    //If we got this far, something failed, redisplay form
        //    return View(model);
        //}


        //public ActionResult Completed(string successOrderList)
        //{
        //    //validation
        //    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
        //        return new HttpUnauthorizedResult();

        //    //model
        //    var model = new CheckoutCompletedModel();

        //    //var orders = _orderService.GetOrdersByCustomerId(_workContext.CurrentCustomer.Id);
        //    //if (orders.Count == 0)
        //    //    return RedirectToRoute("HomePage");
        //    //else
        //    //{
        //    //    var lastOrder = orders[0];
        //    //    model.OrderId = lastOrder.Id;
        //    //}

        //    if (!String.IsNullOrEmpty(successOrderList))
        //    {
        //        model.OrderIds = successOrderList.Split(',');
        //        Array.ForEach(model.OrderIds, s => s.Trim());
        //        model.OrderId = 0;
        //    }
        //    else
        //        return RedirectToRoute("HomePage");

        //    foreach (string s in model.OrderIds)
        //    {
        //        int orderId;
        //        if (Int32.TryParse(s, out orderId))
        //        {
        //            var store = _orderService.GetStoreByOrderId(orderId);
        //            model.Stores.Add(store);
        //        }
        //    }

        //    decimal subTotal = 0;
        //    decimal shipping = 0;
        //    IList<TaxSurcharge> taxRates = new List<TaxSurcharge>();
        //    decimal orderTotal = 0;

        //    foreach (var orderId in model.OrderIds)
        //    {
        //        var order = new Order();
        //        order = _orderService.GetOrderById(Convert.ToInt32(orderId));
        //        var trs = _taxService.GetSurchargesByOrder(order).ToList().ToTaxes().ToDistinctTypes();
        //        if (trs.Any())
        //        {
        //            trs.ForEach(x =>
        //                {
        //                    var tr = taxRates.SingleOrDefault(y => y.SurchargeTypeId == x.SurchargeTypeId && (y.IsFixedCharge || y.PercentageOrFee == x.PercentageOrFee));
        //                    if (tr == null)
        //                    {
        //                        taxRates.Add(x);
        //                    }
        //                    else
        //                    {
        //                        tr.Value += x.Value;
        //                    }
        //                });
        //        }


        //        subTotal += order.OrderSubtotalExclTax;
        //        shipping += order.OrderShippingExclTax;
        //        orderTotal += order.OrderTotal;
        //    }

        //    model.OrderTotalsModel.SubTotal = _priceFormatter.FormatPrice(subTotal);
        //    model.OrderTotalsModel.Shipping = _priceFormatter.FormatShippingPrice(shipping, true);

        //    model.OrderTotalsModel.TaxRates = taxRates.Select(x => new TaxRateModel()
        //        {
        //            NameRate = _priceFormatter.FormatTaxRate(x, _taxSettings.DisplayTaxRatePercentage && !x.IsFixedCharge),
        //            Value = _priceFormatter.FormatPrice(_currencyService.ConvertFromPrimaryStoreCurrency(x.Value, _workContext.WorkingCurrency), true, false)
        //        }).ToList();
        //    model.OrderTotalsModel.OrderTotal = _priceFormatter.FormatPrice(orderTotal);
        //    model.BillingAddress.PrepareModel(_workContext.CurrentCustomer.BillingAddress, false, _addressSettings);
        //    model.ShippingAddress.PrepareModel(_workContext.CurrentCustomer.ShippingAddress, false, _addressSettings);

        //    return View(model);
        //}

        #endregion

        public ActionResult CheckoutProgress(CheckoutProgressStep step)
        {
            var model = new CheckoutProgressModel() { CheckoutProgressStep = step };
            return PartialView(model);
        }


        #region Methods (one page checkout)
        [RequireHttpsAttribute]
        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult OnePageCheckout(string type, int? id)
        {
            // Need to make sure page is not cached to avoid double checkout
            Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
            Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
            Response.AppendHeader("Expires", "0"); // Proxies.

            var customer = _workContext.CurrentCustomer;

            if ((customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                return new HttpUnauthorizedResult();

            int? storeId = type == "s" ? id : null;
            int? orderId = type == "o" ? id : null; ;

            string currentIpAddress = _webHelper.GetCurrentIpAddress();
            if (customer.IsRegistered() && (customer.LastIpAddress != currentIpAddress || customer.LastLoginDateUtc < DateTime.UtcNow.AddDays(-7)))
                return RedirectToRoute("Login", new { checkout = true, returnUrl = Url.RouteUrl("CheckoutOnePage", new { type = "s", Id = storeId }) });

            //validation
            if ((!storeId.HasValue && !orderId.HasValue) || (storeId.HasValue && orderId.HasValue && storeId.Value > 0 && orderId.Value > 0))
                return RedirectToRoute("ShoppingCart");

            var model = new OnePageCheckoutModel();

            if (storeId.HasValue && storeId.Value > 0)
            {
                var cart = GetCart(storeId.Value);
                if (cart.Count == 0 && storeId != 1)
                    return RedirectToRoute("ShoppingCart");


                _httpContext.Session["StoreId"] = storeId.Value;

                model = new OnePageCheckoutModel()
                {
                    StoreId = storeId.Value,
                    ShippingRequired = cart.RequiresShipping()
                };
            }
            else if (orderId.HasValue && orderId > 0)
            {
                model = new OnePageCheckoutModel()
                {
                    OrderId = orderId.Value
                };
            }


            //specify the payment method
            //var paymentmethod = _paymentSettings.DefaultPaymentMethodSystemName;

            //var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(paymentmethod);
            //if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
            //    throw new NopException("Payment method can't be parsed: {0}", paymentmethod);

            //_workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentmethod;
            //_customerService.UpdateCustomer(_workContext.CurrentCustomer);

            return View(model);
        }

        //[ChildActionOnly]
        //public ActionResult OpcPaymentForm(int storeId)
        //{
        //    //payment is required
        //    var cart = GetCart();
        //    var paymentMethodModel = PreparePaymentMethodModel(cart);

        //    //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
        //    //so customer doesn't have to choose a payment method
        //    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods.Last().PaymentMethodSystemName;
        //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

        //    var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
        //    if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
        //        throw new Exception("Selected payment method can't be parsed");


        //    var paymenInfoModel = PreparePaymentInfoModel(paymentMethodInst, storeId);
        //    return PartialView("OpcPaymentInfo", paymenInfoModel);
        //}

        public void OpcCheckSteps(CheckoutSteps step, out string title, out List<int> enable, out List<int> disable)
        {
            enable = new List<int>();
            disable = new List<int>();
            title = "";
            if (step == CheckoutSteps.ShippingAddress)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.ShippingAddress").ResourceValue;
                enable.Add((int)CheckoutSteps.ShippingAddress);
                disable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.Complete, (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm });
            }
            if (step == CheckoutSteps.Options)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.Options").ResourceValue;
                enable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress });
                disable.AddRange(new List<int>() { (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm, (int)CheckoutSteps.Complete });
            }
            if (step == CheckoutSteps.BillingAddress)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.BillingAddress").ResourceValue;
                enable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress });
                disable.AddRange(new List<int>() { (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm, (int)CheckoutSteps.Complete });
            }

            if (step == CheckoutSteps.PaymentInfo)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.PaymentInfo").ResourceValue;
                enable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress, (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.PaymentInfo });
                disable.AddRange(new List<int>() { (int)CheckoutSteps.Confirm, (int)CheckoutSteps.Complete });
            }
            if (step == CheckoutSteps.Confirm)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.ConfirmOrder").ResourceValue;
                enable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress, (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm });
                disable.AddRange(new List<int>() { (int)CheckoutSteps.Complete });
            }
            if (step == CheckoutSteps.Complete)
            {
                title = _localizationService.GetLocaleStringResourceByName("Checkout.ThankYou").ResourceValue;
                enable.Add((int)CheckoutSteps.Complete);
                disable.AddRange(new List<int>() { (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress, (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm });
            }
            if (step == CheckoutSteps.Error)
            {
                disable.AddRange(new List<int>() { (int)CheckoutSteps.Complete, (int)CheckoutSteps.ShippingMethod, (int)CheckoutSteps.BillingAddress, (int)CheckoutSteps.ShippingAddress, (int)CheckoutSteps.PaymentInfo, (int)CheckoutSteps.Confirm });
            }
        }

        public ActionResult OpcAjaxView(string viewname = "", object model = null, CheckoutSteps step = CheckoutSteps.ShippingAddress, string type = "", int? id = null)
        {
            var enable = new List<int>();
            var disable = new List<int>();
            var title = "";
            var html = "";
            var url = "";
            var success = false;

            if ((type == "o" || type == "s") && id.HasValue && id > 0)
            {
                url = Url.RouteUrl("CheckoutOnePage", new { type = type, id = id.Value });
            }

            OpcCheckSteps(step, out title, out enable, out disable);
            if (step == CheckoutSteps.Error)
                html = this.RenderPartialViewToString("OpcError");
            else if (!String.IsNullOrEmpty(viewname) && model != null)
            {
                html = this.RenderPartialViewToString(viewname, model);
                if (step == CheckoutSteps.Confirm)
                    success = true;
            }

            return Json(new
            {
                html = html,
                title = title,
                step = (int)step,
                enable = enable.ToArray(),
                disable = disable.ToArray(),
                messages = RenderAjaxNotification(),
                type = type,
                id = id,
                url = url,
                success = success
            });
        }


        [HttpPost]
        public ActionResult OpcLoadCheckOutSteps(int? storeId, int? orderId, bool directCheckout)
        {
            // CheckMessages(); // To do: Check if there are notifications to display to the shopper.
            if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
            {
                ErrorNotification("Anonymous checkout is not allowed");
                return OpcAjaxView("", CheckoutSteps.FatalError);
            }

            if ((storeId.HasValue && orderId.HasValue && storeId.Value > 1 && orderId.Value > 1) || (!storeId.HasValue && !orderId.HasValue))
            {
                ErrorNotification("Cannot determine checkout action.");
            }
            var customer = _workContext.CurrentCustomer;
            if (storeId.HasValue && storeId > 0)
            {
                if (customer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList().Count == 0)
                {
                    ErrorNotification("There is no item in your shopping cart for checkout.");
                    return OpcAjaxView(null, null, CheckoutSteps.Error);
                }

                if (directCheckout && !String.IsNullOrEmpty(customer.SelectedPaymentMethodSystemName))
                {
                    var cart = GetCart(storeId.Value);
                    var paymentRequired = IsPaymentWorkflowRequired(cart, false);
                    var isLastOrderShopping = customer.Orders.LastOrDefault() != null && customer.Orders.Last().OrderKind == OrderKind.Commerce;
                    var hasShipping = customer.ShippingAddress != null && _addressService.GetAddressById(customer.ShippingAddress.Id) != null;
                    var hasBilling = customer.BillingAddress != null && _addressService.GetAddressById(customer.BillingAddress.Id) != null;
                    var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(customer.SelectedPaymentMethodSystemName);
                    var hasDefaultPaymentCreditCard = false;
                    if (paymentMethod != null)
                        hasDefaultPaymentCreditCard = paymentMethod.GetCreditCard(0, storeId.Value, false, true) != null;
                    if (hasShipping && isLastOrderShopping && (!paymentRequired || (hasBilling && hasDefaultPaymentCreditCard)))
                    {
                        // quick payment to confirm
                        var confirmModel = PrepareConfirmOrderModel(cart);
                        return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);
                    }
                }

                var model = PrepareCheckoutAddressModel();
                model.storeId = storeId.Value;
                model.IsShipping = true;
                return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
            }
            else if (orderId.HasValue && orderId > 0)
            {
                // Load existing pending order
                var order = _orderService.GetOrderById(orderId.Value);
                if (order == null || order.CustomerId != customer.Id || order.Deleted || order.OrderStatus != OrderStatus.Pending || order.PaymentStatus != PaymentStatus.Pending)
                {
                    ErrorNotification("Order not exist or you are not allowed to process this order.");
                    return OpcAjaxView("", CheckoutSteps.FatalError);
                }
                var orderSummaryModel = PrepareConfirmOrderModel(null, order);
                return OpcAjaxView("OpcConfirmOrder", orderSummaryModel, CheckoutSteps.Confirm);
            }
            return OpcAjaxView("", CheckoutSteps.FatalError);
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [HttpPost]
        public ActionResult OpcPageRequest(int step, int? storeId, int? request = null, int? orderId = null)
        {
            if (!storeId.HasValue)
                storeId = GetStoreId();
            var customer = _workContext.CurrentCustomer;
            var order = new Order();
            if (orderId.HasValue && orderId > 0)
            {
                order = _orderService.GetOrderById(orderId.Value);
                if (order == null || order.CustomerId != customer.Id || order.Deleted || order.OrderStatus != OrderStatus.Pending || order.PaymentStatus != PaymentStatus.Pending)
                {
                    ErrorNotification("You are not allowed to edit this order.");
                    return OpcAjaxView(null, null, CheckoutSteps.Error);
                }
            }

            switch (step)
            {
                case (int)CheckoutSteps.ShippingAddress:
                    var shipModel = PrepareCheckoutAddressModel(null, true, null, storeId, orderId);
                    shipModel.storeId = storeId.Value;
                    shipModel.IsShipping = true;
                    return OpcAjaxView("OpcAddresses", shipModel, CheckoutSteps.ShippingAddress);

                case (int)CheckoutSteps.BillingAddress:
                    var billModel = PrepareCheckoutAddressModel(null, false, null, storeId, orderId);
                    billModel.storeId = storeId.Value;
                    billModel.IsShipping = false;
                    billModel.Request = request;
                    return OpcAjaxView("OpcAddresses", billModel, CheckoutSteps.PaymentInfo);

                case (int)CheckoutSteps.PaymentMethod:
                case (int)CheckoutSteps.PaymentInfo:
                    var cart = GetCart(storeId.Value);
                    var paymentMethodModel = PreparePaymentMethodModel(cart);

                    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
                        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                    {
                        //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
                        //so customer doesn't have to choose a payment method
                        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
                        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                        var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                        if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                            throw new Exception("Selected payment method can't be parsed");

                        return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart);
                    }
                    else
                    {
                        //customer have to choose a payment method
                        return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentInfo);
                    }

            }

            return OpcAjaxView(null, null, CheckoutSteps.Error);
        }

        [ChildActionOnly]
        public ActionResult OpcAddresses(int storeId, bool isShipping = true, bool? IsReserve = false)
        {
            if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                throw new Exception("Anonymous checkout is not allowed");

            var model = PrepareCheckoutAddressModel();
            model.storeId = storeId;
            model.IsShipping = isShipping;
            if (IsReserve.HasValue && IsReserve.Value)
                model.IsReserve = IsReserve.Value;
            return PartialView(model);
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcSelectAddress(string addressType, int? addressId, int? storeId, int? orderId, bool? IsReserve = false)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                var order = new Order();
                var customer = _workContext.CurrentCustomer;
                if (addressId != null && addressId > 0)
                {
                    var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
                    if (orderId.HasValue && orderId > 0)
                    {
                        order = _orderService.GetOrderById(orderId.Value);
                        ModelState.Clear();
                        if (order == null || order.CustomerId != customer.Id || order.Deleted || order.OrderStatus != OrderStatus.Pending || order.PaymentStatus != PaymentStatus.Pending)
                        {
                            ModelState.AddModelError("", "You are not allowed to edit this order.");
                            return OpcAjaxView(null, null, CheckoutSteps.FatalError);
                        }
                        else if (address != null)
                        {
                            if (addressType == CommonValues.Billing)
                            {
                                order.BillingAddress = address;
                                order.BillingAddressId = address.Id;
                            }
                            else if (addressType == CommonValues.Shipping)
                            {
                                order.ShippingAddress = address;
                                order.ShippingAddressId = address.Id;
                            }
                            _orderService.UpdateOrder(order);
                            var confirmModel = PrepareConfirmOrderModel(null, order);
                            return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);
                        }
                        else
                        {
                            ModelState.AddModelError("", "Address does not exist.");
                            var model = PrepareCheckoutAddressModel(null, addressType == CommonValues.Shipping, null, null, orderId);
                            if (addressType == CommonValues.Shipping)
                            {
                                return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                            }
                            return OpcAjaxView("OpcAddresses", model, CheckoutSteps.BillingAddress);
                        }
                    }
                    //select an existing address
                    if (address == null)
                    {
                        ErrorNotification("Address can't be loaded");
                        var model = PrepareCheckoutAddressModel();
                        model.storeId = storeId.Value;
                        if (addressType == CommonValues.Billing)
                        {
                            model.IsShipping = false;
                            return OpcAjaxView("OpcAddresses", model, CheckoutSteps.BillingAddress);
                        }
                        if (IsReserve.HasValue && IsReserve.Value)
                            model.IsReserve = IsReserve.Value;
                        model.IsShipping = true;
                        return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                    }
                    else
                    {
                        if (addressType == CommonValues.Shipping)
                        {
                            _workContext.CurrentCustomer.ShippingAddress = address;
                            if (!address.IsAddressTypeVerified() && address.TypeId == (int)AddressType.Unknown || address.TypeId == null)
                            {
                                WarningNotification("Please select the type of address.");
                                var model = PrepareCheckoutAddressModel();
                                model.storeId = storeId.Value;
                                model.IsShipping = true;
                                if (IsReserve.HasValue && IsReserve.Value)
                                    model.IsReserve = IsReserve.Value;
                                return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                            }
                        }
                        else if (addressType == CommonValues.Billing)
                        {
                            _workContext.CurrentCustomer.BillingAddress = address;
                        }
                    }
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                    this.ViewData["addressType"] = addressType;
                    if (IsReserve.HasValue && IsReserve.Value)
                        return OpcLoadStepAfterAddress(false, null, null, IsReserve);
                    if (addressType == CommonValues.Billing)
                    {
                        var confirmModel = new CheckoutConfirmModel();
                        if (orderId.HasValue && orderId > 0)
                            confirmModel = PrepareConfirmOrderModel(null, order);
                        else
                            confirmModel = PrepareConfirmOrderModel(GetCart());
                        return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);
                    }
                    //after select should to redirect opcaddress to add shipmenttime
                    var outmodel = PrepareCheckoutAddressModel();
                    outmodel.storeId = storeId.Value;
                    outmodel.IsShipping = true;
                    return OpcAjaxView("OpcAddresses", outmodel, CheckoutSteps.ShippingAddress);
                    //return OpcLoadStepAfterAddress();
                }
                else
                {
                    //clear the address
                    if (addressType == CommonValues.Shipping)
                    {
                        _workContext.CurrentCustomer.ShippingAddress = null;
                    }
                    else if (addressType == CommonValues.Billing)
                    {
                        _workContext.CurrentCustomer.BillingAddress = null;
                    }

                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    var model = PrepareCheckoutAddressModel();
                    model.storeId = storeId.Value;
                    if (addressType == CommonValues.Billing)
                    {
                        SuccessNotification("Billing address has been cleared.");
                        model.IsShipping = false;
                        return OpcAjaxView("OpcAddresses", model, CheckoutSteps.BillingAddress);
                    }
                    SuccessNotification("Shipping address has been cleared.");
                    model.IsShipping = true;
                    return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                ErrorNotification(exc.Message);
                return OpcAjaxView(null, null, CheckoutSteps.Error);
            }
        }

        [HttpPost]
        public ActionResult OpcLoadEditAddressForm(int addressId, bool isShipping, int? storeId = null, int? orderId = null)
        {
            var customer = _workContext.CurrentCustomer;
            var address = customer.Addresses.FirstOrDefault(a => a.Id == addressId);
            ModelState.Clear();
            if (address == null)
            {
                ModelState.AddModelError("", "This address does not exist.");
                return Json(new
                {
                    html = "",
                    messages = RenderAjaxNotification()
                });
            }
            var model = PrepareCheckoutAddressModel(null, isShipping, addressId, storeId, orderId);
            return Json(new
            {
                html = this.RenderPartialViewToString("_RenderAddressForm", model)
            });
        }

        [ValidateInput(false)]
        public ActionResult OpcAddressForm(string addressType, int? addressId)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                this.ViewData["status"] = CommonValues.New;
                CheckoutAddressModel model = PrepareCheckoutAddressModel();
                this.ViewData["addressType"] = addressType;
                this.ViewData["addressId"] = addressId;
                AddressModel addressModel = new AddressModel();
                addressModel.PrepareModel(new Address(), false, _addressSettings);
                if (addressId != null && addressId > 0)
                {
                    //existing address
                    var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
                    if (address == null)
                    {
                        throw new Exception("Address can't be loaded");
                    }
                    else
                    {
                        this.ViewData["status"] = CommonValues.Edit;
                        addressModel.PrepareModel(address, false, _addressSettings);
                    }
                }
                if (addressModel.CountryId == 0 || addressModel.CountryId == null)
                {
                    addressModel.CountryId = _countryService.GetAllCountries().First().Id;
                }
                var states = _stateProvinceService.GetStateProvincesByCountryId(addressModel.CountryId.Value).ToList();
                if (states.Count > 0)
                {
                    addressModel.AvailableStates = states.Select(
                        s => new SelectListItem()
                        {
                            Text = s.Name,
                            Value = s.Id.ToString(),
                            Selected = false
                        }).ToList();
                    addressModel.AvailableStates.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectProvince"), Value = "0", Selected = true });
                }
                model.Address = addressModel;
                var displayAddressForm = this.RenderPartialViewToString("_RenderAddressForm", model);
                return Json(new
                    {
                        addressType = addressType,
                        addressId = addressId,
                        displayAddressForm = displayAddressForm
                    }
                );
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [HttpPost]
        public ActionResult OpcUpdateDisplayAddress(CheckoutAddressModel model, int? checkoutStep, int? storeId, bool? IsReserve = false)
        {
            if (model == null || checkoutStep == null)
            {
                ErrorNotification("Invalid address checkout request.");
                return Json(new
                {
                    message = RenderAjaxNotification()
                });
            }

            if (model.OrderId.HasValue && model.OrderId > 0)
            {
                var order = _orderService.GetOrderById(model.OrderId.Value);
                if (model.IsShipping)
                {
                    var picker = _logisticsService.GetPickerModel((int)OrderKind.Commerce, order.ShippingAddress.VerifiedType.HasValue ? order.ShippingAddress.VerifiedType.Value : order.ShippingAddress.Type.Value, order.ShippingAddress.ZipPostalCode);
                    if (model.HasScheduledDate && !String.IsNullOrEmpty(model.DatePickerTime) && !String.IsNullOrEmpty(model.DatePickerTimeSpan))
                    {
                        DateTime shipmentDate;
                        int shipmentTimeWindow;
                        DateTime.TryParse(model.DatePickerTime, out shipmentDate);
                        Int32.TryParse(model.DatePickerTimeSpan, out shipmentTimeWindow);
                        if (picker.DaysList.Where(d => d.Time == shipmentDate && d.ShipmentTimeSpans.Where(sts => sts.Id == shipmentTimeWindow).Count() > 0).Count() > 0)
                        {
                            order.ShipmentDate = shipmentDate.Date;
                            order.ShippingTimeWindowsId = shipmentTimeWindow;
                        }
                        else
                        {
                            order.ShipmentDate = picker.FirstAvailableDate.Date;
                            order.ShippingTimeWindowsId = picker.FirstAvailableTimeWindow;
                        }
                    }
                    else if (model.HasScheduledDate)
                    {
                        order.ShipmentDate = picker.FirstAvailableDate.Date;
                        order.ShippingTimeWindowsId = picker.FirstAvailableTimeWindow;
                    }
                    else
                    {
                        order.ShipmentDate = null;
                        order.ShippingTimeWindowsId = null;
                    }
                    if (!order.ShippingAddress.IsAddressTypeVerified() && model.Address.TypeId.HasValue)
                    {
                        order.ShippingAddress.TypeId = model.Address.TypeId;
                        order.ShippingAddress.Type = (AddressType)model.Address.TypeId;
                    }
                    _orderService.UpdateOrder(order);
                }
                var confirmModel = PrepareConfirmOrderModel(null, order);
                return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);
            }

            if (model.IsShipping)
            {

                //insert shipmenttime to genericattribute
                if (model.HasScheduledDate && !String.IsNullOrEmpty(model.DatePickerTime) && !String.IsNullOrEmpty(model.DatePickerTimeSpan))
                {
                    InsertScheduledDateTime(model.DatePickerTime, model.DatePickerTimeSpan);
                }
                else
                {
                    RemoveScheduledDateTime();
                }
                var address = _workContext.CurrentCustomer.Addresses.FirstOrDefault(a => a.Id == model.Address.Id);
                if (address == null)
                {
                    ErrorNotification("Invalid address checkout request.");
                    return Json(new
                    {
                        message = RenderAjaxNotification()
                    });
                }
                if (!address.IsAddressTypeVerified())
                {
                    if (model.Address == null || model.Address.TypeId == (int)AddressType.Unknown)
                    {
                        ErrorNotification("You need to select the type of address.");
                        return Json(new
                        {
                            message = RenderAjaxNotification()
                        });
                    }
                    address.TypeId = model.Address.TypeId;
                    address.Type = (AddressType)model.Address.TypeId;

                    //update address
                    _addressService.UpdateAddress(address);

                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                }
            }
            ModelState.Clear();
            if (IsReserve.HasValue && IsReserve.Value)
            {
                return OpcLoadStepAfterAddress(model.IsEditing, checkoutStep, storeId, IsReserve);
            }
            return OpcLoadStepAfterAddress(model.IsEditing, checkoutStep, storeId);
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcSaveAddress(CheckoutAddressModel model, string addressType, int? addressId)
        {
            try
            {
                if (!UseOnePageCheckout())
                    throw new Exception("One page checkout is disabled");

                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                var order = new Order();
                var customer = _workContext.CurrentCustomer;
                if (model.OrderId.HasValue && model.OrderId > 0)
                {
                    order = _orderService.GetOrderById(model.OrderId.Value);

                    if (order == null || order.CustomerId != customer.Id || order.Deleted || order.OrderStatus != OrderStatus.Pending || order.PaymentStatus != PaymentStatus.Pending)
                    {
                        ModelState.AddModelError("", "You are not allowed to edit this order.");
                        return OpcAjaxView(null, null, CheckoutSteps.FatalError);
                    }
                }

                this.ViewData["addressType"] = addressType;
                if (addressId != null && addressId > 0)
                {
                    this.ViewData["status"] = CommonValues.Edit;
                }
                else
                {
                    this.ViewData["status"] = CommonValues.New;
                }

                if (model.IsShipping && !model.Address.IsTypeVerified && (model.Address.TypeId == null || model.Address.TypeId.Value == (int)AddressType.Unknown))
                {
                    ModelState.AddModelError("", "Type of address (business/residential) is required for a shipping address.");
                }
                if (!ModelState.IsValid)
                {
                    //model is not valid. redisplay the form with errors
                    ModelStateErrorNotification();
                    ModelState.Clear();
                    if (model.IsShipping)
                        return OpcAjaxView(null, null, CheckoutSteps.ShippingAddress);
                    return OpcAjaxView(null, null, CheckoutSteps.BillingAddress);
                }
                else
                {
                    ModelState.Clear();
                    //try to find an address with the same values (don't duplicate records)
                    var address = _workContext.CurrentCustomer.Addresses.ToList().FindAddress(
                        model.Address.FirstName, model.Address.LastName,
                        model.Address.PhoneNumber, model.Address.CellPhoneNumber,
                        model.Address.Email, model.Address.FaxNumber, model.Address.Company,
                        model.Address.Address1, model.Address.Address2, model.Address.City,
                        model.Address.StateProvinceId, model.Address.ZipPostalCode, model.Address.CountryId);
                    if (address == null)
                    {
                        //address is not found. let's create a new one
                        address = model.Address.ToEntity();
                        address.CreatedOnUtc = DateTime.UtcNow;
                        //some validation
                        if (address.CountryId == 0)
                            address.CountryId = null;
                        if (address.StateProvinceId == 0)
                            address.StateProvinceId = null;
                        _workContext.CurrentCustomer.Addresses.Add(address);
                    }
                    else
                    {
                        //update address type if not verified
                        if (!address.IsAddressTypeVerified() && model.Address != null)
                        {
                            address.TypeId = model.Address.TypeId;
                            address.Type = (AddressType)model.Address.TypeId;
                        }
                    }
                    if (addressId == null || addressId == 0)
                    {
                        if (model.OrderId.HasValue && model.OrderId > 1)
                        {
                            if (addressType == CommonValues.Shipping.ToString())
                            {
                                order.ShippingAddress = address;
                                order.ShippingAddressId = address.Id;
                            }
                            else if (addressType == CommonValues.Billing.ToString())
                            {
                                order.BillingAddress = address;
                                order.BillingAddressId = address.Id;
                            }
                            _orderService.UpdateOrder(order);
                            var confirmModel = PrepareConfirmOrderModel(null, order);
                            return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);

                        }
                        else
                        {
                            if (addressType == CommonValues.Shipping.ToString())
                                _workContext.CurrentCustomer.ShippingAddress = address;
                            else if (addressType == CommonValues.Billing.ToString())
                                _workContext.CurrentCustomer.BillingAddress = address;
                        }
                    }
                }
                _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                if (!ModelState.IsValid)
                {
                    return Json(new
                    {
                        message = RenderAjaxNotification()
                    });
                }
                else
                {
                    //this.ViewData["addressType"] = addressType;
                    //return OpcLoadStepAfterAddress();
                    var outmodel = PrepareCheckoutAddressModel();
                    outmodel.storeId = 1;
                    outmodel.IsShipping = true;
                    return OpcAjaxView("OpcAddresses", outmodel, CheckoutSteps.ShippingAddress);
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcDeleteAddress(int addressId, string addressType)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                //is existing address
                var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == addressId).FirstOrDefault();
                if (address == null)
                {
                    throw new Exception("Address can't be loaded");
                }
                else
                {
                    if (_workContext.CurrentCustomer.ShippingAddress.Id == addressId)
                    {
                        _workContext.CurrentCustomer.ShippingAddress = null;
                    }
                    if (_workContext.CurrentCustomer.BillingAddress.Id == addressId)
                    {
                        _workContext.CurrentCustomer.BillingAddress = null;
                    }
                    _workContext.CurrentCustomer.Addresses.Remove(address);
                }
                _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                CheckoutAddressModel model = PrepareCheckoutAddressModel();
                this.ViewData["addressType"] = CommonValues.Shipping;
                var availableShippingAddresses = this.RenderPartialViewToString("_AvailableAddresses", model);
                var displayShippingAddress = this.RenderPartialViewToString("_AddressDisplay", model);
                this.ViewData["addressType"] = CommonValues.Billing;
                var availableBillingAddresses = this.RenderPartialViewToString("_AvailableAddresses", model);
                var displayBillingAddress = this.RenderPartialViewToString("_AddressDisplay", model);
                if (_workContext.CurrentCustomer.ShippingAddress != null)
                {
                    model.ShippingAddressId = _workContext.CurrentCustomer.ShippingAddress.Id;
                }
                if (_workContext.CurrentCustomer.BillingAddress != null)
                {
                    model.BillingAddressId = _workContext.CurrentCustomer.BillingAddress.Id;
                }
                this.ViewData["addressType"] = addressType;
                return Json(new
                    {
                        success = CommonValues.True,
                        addressType = addressType,
                        availableShippingAddresses = availableShippingAddresses,
                        availableBillingAddresses = availableBillingAddresses,
                        displayShippingAddress = displayShippingAddress,
                        displayBillingAddress = displayBillingAddress
                    }
                );
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcSaveShippingMethod(FormCollection form, bool? IsReserve = false)
        {
            try
            {
                List<ShoppingCartItem> cart = new List<ShoppingCartItem>();
                if (IsReserve.HasValue && IsReserve.Value)
                {
                    if (Session["ReserveCar"] == null)
                        throw new Exception("Your Reserve is empty");
                    var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                    cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                }
                //validation
                else
                    cart = GetCart();
                if (cart.Count == 0)
                    throw new Exception("Your cart is empty");

                if (!UseOnePageCheckout())
                    throw new Exception("One page checkout is disabled");

                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                if (!cart.RequiresShipping())
                    throw new Exception("Shipping is not required");

                //parse selected method 
                string shippingoption = form["shippingoption"];
                if (String.IsNullOrEmpty(shippingoption))
                    throw new Exception("Selected shipping method can't be parsed");
                var splittedOption = shippingoption.Split(new string[] { "___" }, StringSplitOptions.RemoveEmptyEntries);
                if (splittedOption.Length != 2)
                    throw new Exception("Selected shipping method can't be parsed");
                string selectedName = splittedOption[0];
                string shippingRateComputationMethodSystemName = splittedOption[1];

                //find it
                //performance optimization. try cache first
                var shippingOptions = _workContext.CurrentCustomer.GetAttribute<List<ShippingOption>>(SystemCustomerAttributeNames.OfferedShippingOptions);
                if (shippingOptions == null || shippingOptions.Count == 0)
                {
                    //not found? let's load them using shipping service
                    shippingOptions = _shippingService
                        .GetShippingOptions(cart, _workContext.CurrentCustomer.ShippingAddress, shippingRateComputationMethodSystemName, true)
                        .ShippingOptions
                        .ToList();
                }
                else
                {
                    //loaded cached results. let's filter result by a chosen shipping rate computation method
                    shippingOptions = shippingOptions.Where(so => so.ShippingRateComputationMethodSystemName.Equals(shippingRateComputationMethodSystemName, StringComparison.InvariantCultureIgnoreCase))
                        .ToList();
                }

                var shippingOption = shippingOptions
                    .Find(so => !String.IsNullOrEmpty(so.Name) && so.Name.Equals(selectedName, StringComparison.InvariantCultureIgnoreCase));
                if (shippingOption == null)
                    throw new Exception("Selected shipping method can't be loaded");

                //save
                _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.LastShippingOption, shippingOption);


                //Check whether payment workflow is required
                //we ignore reward points during cart total calculation
                bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart, true);
                if (isPaymentWorkflowRequired)
                {
                    //payment is required
                    var paymentMethodModel = PreparePaymentMethodModel(cart);

                    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
                        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                    {
                        //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
                        //so customer doesn't have to choose a payment method
                        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
                        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                        var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                        if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                            throw new Exception("Selected payment method can't be parsed");
                        if (IsReserve.HasValue && IsReserve.Value)
                            return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart, IsReserve.Value);
                        return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart);
                    }
                    else
                    {
                        if (IsReserve.HasValue && IsReserve.Value)
                            paymentMethodModel.IsReserve = IsReserve.Value;
                        //customer have to choose a payment method
                        return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentMethod);
                    }
                }
                else
                {
                    //payment is not required
                    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    var confirmOrderModel = PrepareConfirmOrderModel(cart);
                    if (IsReserve.HasValue && IsReserve.Value)
                        confirmOrderModel.IsReserve = IsReserve.Value;
                    return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcSavePaymentMethod(FormCollection form, bool? IsReserve)
        {
            try
            {
                List<ShoppingCartItem> cart = new List<ShoppingCartItem>();
                if (IsReserve.HasValue && IsReserve.Value)
                {
                    if (Session["ReserveCar"] == null)
                        throw new Exception("Your Reserve is empty");
                    var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                    cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                }
                //validation
                else
                    cart = GetCart();
                if (cart.Count == 0)
                    throw new Exception("Your cart is empty");

                if (!UseOnePageCheckout())
                    throw new Exception("One page checkout is disabled");

                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                string paymentmethod = form["paymentmethod"];
                //payment method 
                if (String.IsNullOrEmpty(paymentmethod))
                    throw new Exception("Selected payment method can't be parsed");


                var model = new CheckoutPaymentMethodModel();
                TryUpdateModel(model);

                //reward points
                _workContext.CurrentCustomer.UseRewardPointsDuringCheckout = model.UseRewardPoints;
                _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                //Check whether payment workflow is required
                bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart);
                if (!isPaymentWorkflowRequired)
                {
                    //payment is not required
                    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    var confirmOrderModel = PrepareConfirmOrderModel(cart);
                    if (IsReserve.HasValue && IsReserve.Value)
                        confirmOrderModel.IsReserve = IsReserve.Value;
                    return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                }

                var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(paymentmethod);
                if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                    throw new Exception("Selected payment method can't be parsed");

                //save
                _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentmethod;
                _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                if (IsReserve.HasValue && IsReserve.Value)
                    return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart, IsReserve.Value);
                return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart);
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }
        [HttpPost]
        public ActionResult OpcLoadStepAfterAddress(bool isEditing = false, int? checkoutStep = null, int? storeId = null, bool? IsReserve = false)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                if (_workContext.CurrentCustomer.ShoppingCartItems.Count == 0)
                {
                    ErrorNotification("There is no item in your shopping cart for checkout.");
                    return OpcAjaxView(null, null, CheckoutSteps.Error);
                }

                if (_workContext.CurrentCustomer.ShippingAddress == null)
                {
                    var model = PrepareCheckoutAddressModel();
                    model.storeId = storeId.HasValue ? storeId.Value : 0;
                    model.IsShipping = true;
                    if (IsReserve.HasValue)
                    {
                        model.IsReserve = IsReserve.Value;
                    }
                    ErrorNotification("Shipping address is not set.");
                    return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                }
                if (_workContext.CurrentCustomer.BillingAddress == null || (checkoutStep.HasValue && checkoutStep.Value == (int)CheckoutSteps.BillingAddress))
                {
                    var model = PrepareCheckoutAddressModel(null, false);
                    model.storeId = storeId.HasValue ? storeId.Value : 0;
                    model.IsShipping = false;
                    if (IsReserve.HasValue)
                    {
                        model.IsReserve = IsReserve.Value;
                    }
                    return OpcAjaxView("OpcAddresses", model, CheckoutSteps.BillingAddress);
                }
                else
                {
                    var cart = new List<ShoppingCartItem>();
                    if (IsReserve.HasValue && IsReserve.Value)
                    {
                        if (Session["ReserveCar"] == null)
                            ErrorNotification("Reserve product is empty.");
                        var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                        cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                    }
                    else if (storeId.HasValue)
                    {
                        cart = GetCart(storeId.Value);
                    }
                    else
                        cart = GetCart();
                    var getShippingOptionResponse = _shippingService.GetShippingOptions(cart, _workContext.CurrentCustomer.ShippingAddress, "", true);
                    if (getShippingOptionResponse.Success && getShippingOptionResponse.ShippingOptions.Count > 0)
                    {
                        _genericAttributeService.SaveAttribute(
                            _workContext.CurrentCustomer,
                            SystemCustomerAttributeNames.LastShippingOption,
                            getShippingOptionResponse.ShippingOptions.First());
                    }
                    else
                    {
                        var model = PrepareCheckoutAddressModel();
                        model.storeId = storeId.HasValue ? storeId.Value : 0;
                        model.IsShipping = true;
                        if (IsReserve.HasValue)
                        {
                            model.IsReserve = IsReserve.Value;
                        }
                        foreach (var error in getShippingOptionResponse.Errors)
                        {
                            model.Warnings.Add(error);
                            ErrorNotification(error);
                        }
                        return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
                    }

                    //Check whether payment workflow is required
                    //we ignore reward points during cart total calculation


                    //bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart, true);
                    //if (isPaymentWorkflowRequired && !isEditing)
                    //{
                    //    //payment is required
                    //    var paymentMethodModel = PreparePaymentMethodModel(cart);

                    //    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
                    //        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                    //    {
                    //        //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
                    //        //so customer doesn't have to choose a payment method
                    //        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
                    //        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    //        var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                    //        if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                    //            throw new Exception("Selected payment method can't be parsed");
                    //        if (IsReserve.HasValue)
                    //            return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart, IsReserve.Value);
                    //        return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart);
                    //    }
                    //    else
                    //    {
                    //        //customer have to choose a payment method
                    //        return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentInfo);
                    //    }
                    //}
                    //else
                    //{
                    //    //payment is not required
                    //    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
                    //    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    //    var confirmOrderModel = PrepareConfirmOrderModel(cart);
                    //    if (IsReserve.HasValue)
                    //    {
                    //        confirmOrderModel.IsReserve = IsReserve.Value;
                    //    }
                    //    return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                    //}

                    //payment is not required
                    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    var confirmOrderModel = PrepareConfirmOrderModel(cart);
                    if (IsReserve.HasValue)
                    {
                        confirmOrderModel.IsReserve = IsReserve.Value;
                    }
                    return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NonAction]
        protected ActionResult OpcLoadStepAfterPaymentMethod(IPaymentMethod paymentMethod, List<ShoppingCartItem> cart, bool IsReserve = false, Order order = null)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");

                if (paymentMethod.SkipPaymentInfo && cart != null)
                {
                    //skip payment info page
                    var paymentInfo = new ProcessPaymentRequest();
                    //session save
                    _httpContext.Session["OrderPaymentInfo"] = paymentInfo;

                    var confirmOrderModel = PrepareConfirmOrderModel(cart);
                    confirmOrderModel.IsReserve = IsReserve;
                    if (order != null)
                    {
                        confirmOrderModel.Order = order;
                    }
                    return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                }
                else
                {

                    //return payment info page
                    CheckoutPaymentInfoModel paymenInfoModel;

                    if (order != null)
                    {
                        paymenInfoModel = PreparePaymentInfoModel(paymentMethod, GetStoreId(order.Id));
                        paymenInfoModel.order = order;
                    }
                    else
                    {
                        paymenInfoModel = PreparePaymentInfoModel(paymentMethod, GetStoreId());
                    }

                    paymenInfoModel.IsReserve = IsReserve;
                    return OpcAjaxView("OpcPaymentInfo", paymenInfoModel, CheckoutSteps.PaymentInfo, order != null ? "o" : "s", order != null ? order.Id : 1);
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [ValidateInput(false)]
        public ActionResult OpcSavePaymentInfo(FormCollection form, bool? IsReserve = false)
        {
            try
            {
                ModelState.Clear();
                var cart = new List<ShoppingCartItem>();
                if (IsReserve.HasValue && IsReserve.Value)
                {
                    if (Session["ReserveCar"] == null)
                        throw new Exception("Your ReserveCar is empty");
                    var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                    cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                }
                else
                {
                    cart = GetCart();
                }
                //validation
                //if (cart.Count == 0)
                //    throw new Exception("Your cart is empty");

                if (!UseOnePageCheckout())
                    ModelState.AddModelError("", "One page checkout is disabled");

                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    ModelState.AddModelError("", "Anonymous checkout is not allowed");

                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                if (paymentMethod == null)
                    ModelState.AddModelError("", "Payment method is not selected");

                var paymentControllerType = paymentMethod.GetControllerType();
                var paymentController = DependencyResolver.Current.GetService(paymentControllerType) as BaseNopPaymentController;

                var orderId = int.Parse(form["orderId"]);
                var order = _orderService.GetOrderById(orderId);
                if (order == null)
                {
                    ErrorNotification(new Exception("order is not exist"));
                    return OpcAjaxView("", null, CheckoutSteps.FatalError);
                }
                if (order.Deleted)
                {
                    ErrorNotification(new Exception("order is not exist"));
                    return OpcAjaxView("", null, CheckoutSteps.FatalError);
                }

                order.PaymentMethodSystemName = _workContext.CurrentCustomer.SelectedPaymentMethodSystemName;

                //vertify product variant quantity
                VertifyProVariantQuantity(order.Id);

                //vertify order's creat time ,if nearly more than five hourse ,update order createUTCTime
                VertifyOrUpdatePayTime(order);

                var customerId = _workContext.CurrentCustomer.Id;

                //if (!String.IsNullOrEmpty(form["newCard"]) && !String.IsNullOrEmpty(form["CustomerId"]))
                if (!String.IsNullOrEmpty(form["newCard"]))
                {
                    var orderStoreId = order.StoreId != null ? order.StoreId.Value : 0;
                    var newCard = paymentMethod.GetCreditCard(0, orderStoreId, true, false);

                    if (newCard == null || newCard.CustomerId != customerId)
                    {
                        ModelState.AddModelError("", "Credit card is not valid.");
                        var paymentInfoModel1 = PreparePaymentInfoModel(paymentMethod, orderStoreId);
                        paymentInfoModel1.order = order;
                        return OpcAjaxView("OpcPaymentInfo", paymentInfoModel1, CheckoutSteps.PaymentInfo);
                    }
                    form["CreditCardId"] = newCard.Id.ToString();
                }

                var warnings = paymentController.ValidatePaymentForm(form);
                foreach (var warning in warnings)
                {
                    ModelState.AddModelError("", warning);
                    ErrorNotification(warning);
                }

                if (ModelState.IsValid)
                {					
                    //get payment info
                    var paymentInfo = paymentController.GetPaymentInfo(form);

                    if (paymentInfo.CreditCardId > 0)
                    {
                        paymentMethod.SetDefaultCreditCard(paymentInfo.CreditCardId, paymentInfo.CustomerId, paymentInfo.StoreId);
                    }

                    var processPaymentRequest = new ProcessPaymentRequest()
                    {
                        CustomerId = order.CustomerId,
                        OrderId = order.Id,
                        OrderTotal = order.OrderTotal,
                        CreditCardId = paymentInfo.CreditCardId,
                        StoreId = order.StoreId.Value,
                        PaymentMethodSystemName = order.PaymentMethodSystemName
                    };
                    var result = _paymentService.ProcessPayment(processPaymentRequest);
					
                    if (result.Success)
                    {
                        order.PaymentStatus = PaymentStatus.Paid;
                        order.PaymentStatusId = (int)PaymentStatus.Paid;
                        order.OrderStatusId = (int)OrderStatus.Processing;
                        _orderService.UpdateOrder(order);

                        UpdateProductVariants(order.OrderProductVariants);

                        //create invoice
                        //try
                        //{
                        //    _invoiceService.CreateSingleInvoice(order.Id, _workContext.CurrentCustomer, InvoiceStatus.Paid);
                        //}
                        //catch (Exception exception)
                        //{
                        //    _logger.Error("create invoice when paid commerce order error,error info: " + exception.ToString());
                        //}

                        var opcCompleteModel = new CheckoutCompletedModel();
                        opcCompleteModel.OrderId = order.Id;
                        //opcCompleteModel.OrderIds = result.PlacedOrders.Select(o => o.Id.ToString()).ToArray<string>();
                        opcCompleteModel.IsReserve = IsReserve.Value;

                        int orderPlacedCustomerNotificationQueuedEmailId = _workflowMessageService.SendOrderPlacedCustomerNotification(order, order.CustomerLanguageId);
                        if (orderPlacedCustomerNotificationQueuedEmailId > 0)
                        {
                            order.OrderNotes.Add(new OrderNote()
                            {
                                Note = string.Format("\"Order placed\" email (to customer) has been queued. Queued email identifier: {0}.", orderPlacedCustomerNotificationQueuedEmailId),
                                DisplayToCustomer = false,
                                CreatedOnUtc = DateTime.UtcNow
                            });
                            _orderService.UpdateOrder(order);
                        }

                        return OpcAjaxView("OpcCompleted", opcCompleteModel, CheckoutSteps.Complete);
                        //return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                    }
                    else
                    {
                        foreach (var error in result.Errors)
                        {
                            ErrorNotification(error);
                        }
                    }
                }

                //If we got this far, something failed, redisplay form
                var paymentInfoModel = PreparePaymentInfoModel(paymentMethod);
                if (order != null)
                    paymentInfoModel.order = order;
                paymentInfoModel.Warnings = warnings.ToList();
                foreach (var warning in warnings)
                {
                    ErrorNotification(warning);
                }
                if (IsReserve.HasValue && IsReserve.Value)
                    paymentInfoModel.IsReserve = IsReserve.Value;
                return OpcAjaxView("OpcPaymentInfo", paymentInfoModel, CheckoutSteps.PaymentInfo);
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        private bool UpdateProductVariants(ICollection<OrderProductVariant> OrderProductVariants)
        {
            //_productService.GetProductVariantById()
            if (OrderProductVariants != null)
            {
                foreach (var orderProduct in OrderProductVariants)
                {
                    var productVariant = _productService.GetProductVariantById(orderProduct.ProductVariantId);
                    //if ((productVariant.StockQuantity - productVariant.MinStockQuantity) - orderProduct.Quantity < 0)
                    //{
                    //   throw new Exception("This product is sold out.");
                    //}
                    //productVariant.StockQuantity=productVariant.StockQuantity - orderProduct.Quantity;

                    //_productService.UpdateProductVariant(productVariant);

                    _productService.AdjustInventory(orderProduct.ProductVariant, true, orderProduct.Quantity, orderProduct.AttributesXml);
                }
            }
            return true;
        }

        private bool PaymentOrder(int orderId, int creditCardId, IPaymentMethod paymentMethod)
        {
            var order = _orderService.GetOrderById(orderId);
            var message = "";
            var creditCard = paymentMethod.GetCreditCard(creditCardId);
            if (creditCard == null)
            {
                //return new { success = false, message = "Get CreditCard error" };
                return false;
            }
            string tokenId = creditCard.Token;
            var payOrder = paymentMethod.AddPaymentsTransaction(order.Id, order.OrderTotal, creditCardId, tokenId, out message);
            if (payOrder)
            {
                return true;
                //return new { success = true };
            }
            return false;
        }

        [HttpPost]
        public ActionResult OpcEditSection(string sectionName, bool? IsReserve = false)
        {
            try
            {
                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    throw new Exception("Anonymous checkout is not allowed");
                List<ShoppingCartItem> cart = new List<ShoppingCartItem>();
                if (IsReserve.HasValue && IsReserve.Value)
                {
                    if (Session["ReserveCar"] == null)
                        throw new Exception("ReserveCart  is Empty");
                    var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                    cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                }
                else
                    cart = GetCart();
                string section = sectionName;
                string updateHtml = "";
                switch (sectionName)
                {
                    case "shipping-address":
                        var shippingAddressModel = PrepareCheckoutAddressModel();
                        shippingAddressModel.storeId = 0;
                        shippingAddressModel.IsShipping = true;
                        shippingAddressModel.IsEditing = true;
                        if (IsReserve.HasValue)
                            shippingAddressModel.IsReserve = IsReserve.Value;
                        updateHtml = this.RenderPartialViewToString("OpcAddresses", shippingAddressModel);
                        break;
                    case "billing-address":
                        var billingAddressModel = PrepareCheckoutAddressModel();
                        billingAddressModel.storeId = 0;
                        billingAddressModel.IsShipping = false;
                        billingAddressModel.IsEditing = true;
                        if (IsReserve.HasValue)
                            billingAddressModel.IsReserve = IsReserve.Value;
                        updateHtml = this.RenderPartialViewToString("OpcAddresses", billingAddressModel);
                        break;
                    case "payment-methods":
                        var paymentMethodModel = PreparePaymentMethodModel(cart);
                        if (IsReserve.HasValue)
                            paymentMethodModel.IsReserve = IsReserve.Value;
                        updateHtml = this.RenderPartialViewToString("OpcPaymentMethods", paymentMethodModel);
                        break;
                }
                return Json(new
                {
                    updateHtml = updateHtml,
                    section = sectionName
                });
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        [HttpPost]
        public ActionResult OpcConfirmOrder(bool? IsReserve = false, int? OrderId = null)
        {
            try
            {
                var customer = _workContext.CurrentCustomer;
                if (OrderId.HasValue && OrderId > 0)
                {
                    var pendingOrder = _orderService.GetOrderById(OrderId.Value);
                    if (pendingOrder == null || pendingOrder.CustomerId != customer.Id || pendingOrder.Deleted || pendingOrder.OrderStatus != OrderStatus.Pending || pendingOrder.PaymentStatus != PaymentStatus.Pending)
                    {
                        ErrorNotification("Order not exist or you are not allowed to process this order.");
                        return OpcAjaxView("", CheckoutSteps.FatalError);
                    }
                    return ToPaymentPage(IsReserve, null, null, pendingOrder);
                }
                else
                {
                    var storeId = GetStoreId();
                    List<ShoppingCartItem> cart = new List<ShoppingCartItem>();
                    if (IsReserve.HasValue && IsReserve.Value)
                    {
                        if (Session["ReserveCar"] == null)
                            throw new Exception("Your Reserve is empty");
                        var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                        cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                    }
                    //validation
                    else
                        cart = GetCart(storeId);
                    if (cart.Count == 0)
                        throw new Exception("Your cart is empty");

                    if (!UseOnePageCheckout())
                        throw new Exception("One page checkout is disabled");

                    if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                        throw new Exception("Anonymous checkout is not allowed");

                    //prevent 2 orders being placed within an X seconds time frame
                    if (!IsMinimumOrderPlacementIntervalValid(_workContext.CurrentCustomer))
                        throw new Exception(_localizationService.GetResource("Checkout.MinOrderPlacementInterval"));

                    string successOrderslist = "";
                    var storeOrder = _storeService.GetStoreShoppingCartItems(cart);

                    //place order
                    //var processPaymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest;
                    //if (processPaymentRequest == null)
                    //{
                    //    //Check whether payment workflow is required
                    //    if (IsPaymentWorkflowRequired(cart))
                    //    {
                    //        throw new Exception("Payment information is not entered");
                    //    }
                    //    else
                    //        processPaymentRequest = new ProcessPaymentRequest();
                    //}

                    var processPaymentRequest = new ProcessPaymentRequest();
                    processPaymentRequest.CustomerId = _workContext.CurrentCustomer.Id;
                    processPaymentRequest.PaymentMethodSystemName = _workContext.CurrentCustomer.SelectedPaymentMethodSystemName;

                    storeOrder.ProcessPaymentRequest = processPaymentRequest;
                    PlaceStoreOrderResult result = new PlaceStoreOrderResult();


                    var confirmOrderModel = new CheckoutConfirmModel();
                    //verify productvariant stockQuantity 
                    foreach (var entry in storeOrder.ShoppingCarts)
                    {
                        VertifyProVariantQuantity(entry.Value);
                    }

                    result = _orderProcessingService.PlaceOrder(storeOrder, true);


                    if (result.Success)
                    {
                        //delete genericattribute of shiptime
                        RemoveScheduledDateTime();

                        foreach (var order in result.PlacedOrders)
                        {
                            //var postProcessPaymentRequest = new PostProcessPaymentRequest()
                            //{
                            //    Order = order
                            //};
                            return ToPaymentPage(IsReserve, storeOrder, cart, order);
                            //_paymentService.PostProcessPayment(postProcessPaymentRequest);
                        }
                        if (IsReserve.HasValue && IsReserve.Value)
                        {
                            Session.Remove("ReserveCar");
                        }
                    }
                    else
                    {
                        confirmOrderModel = PrepareConfirmOrderModel(cart);
                        foreach (var error in result.Errors)
                        {
                            confirmOrderModel.Warnings.Add(error);
                            ErrorNotification(error);
                        }
                    }

                    successOrderslist = String.Join(",",
                        result.PlacedOrders.Select(o => o.Id.ToString()));

                    if (confirmOrderModel.Warnings.Count > 0)
                    {
                        var shippingOption = _workContext.CurrentCustomer.GetAttribute<ShippingOption>(SystemCustomerAttributeNames.LastShippingOption);
                        if (shippingOption != null)
                        {
                            confirmOrderModel.ShippingRateComputationMethodSystemName = _shippingService.LoadShippingRateComputationMethodBySystemName(shippingOption.ShippingRateComputationMethodSystemName).GetLocalizedFriendlyName(_localizationService, _workContext.WorkingLanguage.Id);
                        }
                        if (IsReserve.HasValue && IsReserve.Value)
                        {
                            confirmOrderModel.IsReserve = IsReserve.Value;
                        }
                        return OpcAjaxView("OpcConfirmOrder", confirmOrderModel, CheckoutSteps.Confirm);
                    }
                    else
                    {
                        successOrderslist = String.Join(",",
                            result.PlacedOrders.Select(o => o.Id.ToString()));

                        _httpContext.Session["OrderPaymentInfo"] = null;

                        //success
                        var opcCompleteModel = new CheckoutCompletedModel();
                        var orders = _orderService.GetOrdersByCustomerId(_workContext.CurrentCustomer.Id);

                        var lastOrder = orders[0];
                        opcCompleteModel.OrderId = lastOrder.Id;
                        opcCompleteModel.OrderIds = result.PlacedOrders.Select(o => o.Id.ToString()).ToArray<string>();
                        opcCompleteModel.IsReserve = IsReserve.Value;
                        return OpcAjaxView("OpcCompleted", opcCompleteModel, CheckoutSteps.Complete);
                    }
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                //return Json(new { error = 1, message = exc.Message });
                ErrorNotification(exc.Message);
                return OpcAjaxView("", null, CheckoutSteps.FatalError);
            }
        }

        private void VertifyProVariantQuantity(IReadOnlyCollection<ShoppingCartItem> shoppingCartItems)
        {
            if (shoppingCartItems == null || shoppingCartItems.Count <= 0) return;

            foreach (var item in shoppingCartItems)
            {
                var variant = _productService.GetProductVariantById(item.ProductVariantId);
                var quantity = variant.StockQuantity - variant.MinStockQuantity;
                if (quantity >= item.Quantity) continue;

                var product = _productService.GetProductById(variant.ProductId);

                throw new NopException(string.Format("{0} is sold out", product.Name));
            }
        }

        private void VertifyProVariantQuantity(int orderId)
        {
            if (orderId <= 0) return;

            var orderVariants = _orderService.GetAllOrderProductVariants(orderId, _workContext.CurrentCustomer.Id, null,
                null, null, null, null);

            if (orderVariants == null || orderVariants.Count <= 0) return;

            foreach (var item in orderVariants)
            {
                var varitant = _productService.GetProductVariantById(item.ProductVariantId);
                var quantity = varitant.StockQuantity - varitant.MinStockQuantity;
                if (quantity >= item.Quantity) return;

                var product = _productService.GetProductById(varitant.ProductId);

                throw new NopException(string.Format("{0} is sold out", product.Name));
            }
        }

        public void VertifyOrUpdatePayTime(Order order)
        {
            if (order == null) return;

            var utcStart = DateTime.UtcNow.AddHours(-5);
            var utcEnd = DateTime.UtcNow.AddHours(-4).AddMinutes(-50);

            if (order.CreatedOnUtc < utcStart || order.CreatedOnUtc > utcEnd) return;

            order.CreatedOnUtc = order.CreatedOnUtc.AddMinutes(10);
            _orderService.UpdateOrder(order);
        }

        [NopHttpsRequirement(SslRequirement.Yes)]
        public ActionResult ToPaymentPage(bool? IsReserve, StoreOrder storeOrder, List<ShoppingCartItem> cart, Order order)
        {
            #region get PaymentModel
            //if (IsReserve.HasValue && IsReserve.Value)
            //{
            //    result = _orderProcessingService.ReservePlaceOrder(storeOrder);
            //}
            //else
            //{
            var paymentMethodModel = new CheckoutPaymentMethodModel();
            if (storeOrder != null && cart != null)
            {
                bool isPaymentWorkflowRequired = IsPaymentWorkflowRequired(cart, true);

                if (isPaymentWorkflowRequired)
                {
                    paymentMethodModel.Order = order;
                    //payment is required begin
                    paymentMethodModel = PreparePaymentMethodModel(cart);

                    if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
                        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                    {
                        //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
                        //so customer doesn't have to choose a payment method
                        _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
                        _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                        var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                        if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                            throw new Exception("Selected payment method can't be parsed");
                        if (IsReserve.HasValue)
                            return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart, IsReserve.Value, order);
                        return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart, false, order);
                    }
                    else
                    {
                        //customer have to choose a payment method
                        return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentInfo);
                    }

                    //payment is required end
                }
                else
                {
                    //payment is not required
                    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = "";
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);
                    var confirmOrder = PrepareConfirmOrderModel(cart);
                    if (IsReserve.HasValue)
                    {
                        confirmOrder.IsReserve = IsReserve.Value;
                    }
                }
            }
            else
            {
                // Payment for pending order
                paymentMethodModel.Order = order;
                paymentMethodModel = PreparePaymentMethodModel(null);
                if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
                        paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
                {
                    //if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
                    //so customer doesn't have to choose a payment method
                    _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
                    _customerService.UpdateCustomer(_workContext.CurrentCustomer);

                    var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(_workContext.CurrentCustomer.SelectedPaymentMethodSystemName);
                    if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                        throw new Exception("Selected payment method can't be parsed");
                    if (IsReserve.HasValue)
                        return OpcLoadStepAfterPaymentMethod(paymentMethodInst, null, IsReserve.Value, order);
                    return OpcLoadStepAfterPaymentMethod(paymentMethodInst, null, false, order);
                }
                else
                {
                    //customer have to choose a payment method
                    return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentInfo);
                }
            }
            return OpcAjaxView("OpcPaymentMethods", paymentMethodModel, CheckoutSteps.PaymentInfo);
            //}
            #endregion
        }

        public ActionResult OpcCompleteRedirectionPayment()
        {
            try
            {
                //validation
                if (!UseOnePageCheckout())
                    return RedirectToRoute("HomePage");

                if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                    return new HttpUnauthorizedResult();

                //get the order
                var orders = _orderService.GetOrdersByCustomerId(_workContext.CurrentCustomer.Id);
                if (orders.Count == 0)
                    return RedirectToRoute("HomePage");


                var order = orders[0];
                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
                if (paymentMethod == null)
                    return RedirectToRoute("HomePage");
                if (paymentMethod.PaymentMethodType != PaymentMethodType.Redirection)
                    return RedirectToRoute("HomePage");

                //ensure that order has been just placed
                if ((DateTime.UtcNow - order.CreatedOnUtc).TotalMinutes > 3)
                    return RedirectToRoute("HomePage");


                //Redirection will not work on one page checkout page because it's AJAX request.
                //That's why we process it here
                var postProcessPaymentRequest = new PostProcessPaymentRequest()
                {
                    Order = order
                };

                _paymentService.PostProcessPayment(postProcessPaymentRequest);

                if (_webHelper.IsRequestBeingRedirected || _webHelper.IsPostBeingDone)
                {
                    //redirection or POST has been done in PostProcessPayment
                    return Content("Redirected");
                }
                else
                {
                    //if no redirection has been done (to a third-party payment page)
                    //theoretically it's not possible
                    //TODO: store check here
                    //return RedirectToRoute("CheckoutCompleted", new { successOrderList = "54,55" });
                    return RedirectToRoute("HomePage");
                }
            }
            catch (Exception exc)
            {
                _logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
                return Content(exc.Message);
            }
        }

        [HttpPost]
        public string GetValidatedPickupDays(string addressType, string addressId)
        {
            var addressTypeId = int.Parse(addressType);

            var addId = int.Parse(addressId);

            var address = _addressService.GetAddressById(addId);

            var result = GetPickerModel((AddressType)addressTypeId, address.ZipPostalCode);

            return JsonConvert.SerializeObject(result);
        }

        private DatePickerModel GetPickerModel(AddressType addressTypeId, string zipPostalCode, DateTime? inputDate = null)
        {
            return _workContext.CurrentCustomer.ShippingAddress.Type != null ? _logisticsService.GetPickerModel((int)OrderKind.Commerce, addressTypeId, zipPostalCode, inputDate) : new DatePickerModel();
        }

        #endregion

        #region Reserve ProductVariant
        public ActionResult PreOrderOnePageCheckOut(int? storeId)
        {
            var customer = _workContext.CurrentCustomer;
            if (customer == null || customer.IsGuest())
                return RedirectToRoute("HomePage");
            if (Session["ReserveCar"] == null)
                return RedirectToRoute("HomePage");
            var reserveCar = (ReserveCarModel)Session["ReserveCar"];

            //if ((customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
            //    return new HttpUnauthorizedResult();
            string currentIpAddress = _webHelper.GetCurrentIpAddress();
            if (customer.IsRegistered() && (customer.LastIpAddress != currentIpAddress || customer.LastLoginDateUtc < DateTime.UtcNow.AddDays(-7)))
                return RedirectToRoute("Login", new { checkout = true, returnUrl = Url.RouteUrl("PreOrderOnePageCheckOut", new { storeId = storeId }) });
            //validation
            if (!storeId.HasValue)
                return RedirectToRoute("HomePage");

            //var cart = GetCart(storeId.Value);
            var cart = new List<ShoppingCartItem>();
            if (_httpContext.Session["ReserveCar"] != null)
            {
                cart = (ReserveCarModel.ConvertToShoppingCarItem((ReserveCarModel)_httpContext.Session["ReserveCar"], _productService, _workContext, _storeService));
            }

            if (cart.Count == 0 && storeId != 1)
                return RedirectToRoute("HomePage");
            ;
            _httpContext.Session["StoreId"] = storeId.Value;

            var model = new OnPagePreOrderModel()
            {
                StoreId = storeId.Value,
                IsReserve = true,
                ShippingRequired = _productService.GetProductVariantById(reserveCar.ProductVariantId).IsShipEnabled
            };
            return View(model);
        }
        public ActionResult OpcLoadCheckOutStepsByReserve(int storeId, bool quickCheckout)
        {
            // CheckMessages(); // To do: Check if there are notifications to display to the shopper.
            if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
                throw new Exception("Anonymous checkout is not allowed");

            var customer = _workContext.CurrentCustomer;

            if (quickCheckout && !String.IsNullOrEmpty(customer.SelectedPaymentMethodSystemName))
            {
                var reserveCar = (ReserveCarModel)Session["ReserveCar"];
                var cart = ReserveCarModel.ConvertToShoppingCarItem(reserveCar, _productService, _workContext, _storeService);
                var paymentRequired = IsPaymentWorkflowRequired(cart, false);
                var isLastOrderShopping = customer.Orders.LastOrDefault() != null && customer.Orders.Last().OrderKind == OrderKind.Commerce;
                var hasShipping = customer.ShippingAddress != null && _addressService.GetAddressById(customer.ShippingAddress.Id) != null;
                var hasBilling = customer.BillingAddress != null && _addressService.GetAddressById(customer.BillingAddress.Id) != null;
                var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(customer.SelectedPaymentMethodSystemName);
                var hasDefaultPaymentCreditCard = false;
                if (paymentMethod != null)
                    hasDefaultPaymentCreditCard = paymentMethod.GetCreditCard(0, storeId, false, true) != null;
                if (hasShipping && isLastOrderShopping && (!paymentRequired || (hasBilling && hasDefaultPaymentCreditCard)))
                {
                    // quick payment to confirm
                    var confirmModel = PrepareConfirmOrderModel(cart);
                    confirmModel.IsReserve = true;
                    return OpcAjaxView("OpcConfirmOrder", confirmModel, CheckoutSteps.Confirm);
                }
            }

            var model = PrepareCheckoutAddressModel();
            model.storeId = storeId;
            model.IsShipping = true;
            model.IsReserve = true;
            return OpcAjaxView("OpcAddresses", model, CheckoutSteps.ShippingAddress);
        }
        #endregion
        #region Mobile page show

        public ActionResult MobileShow(string uID)
        {
            //var url = "https://safe.sandbox-gtpaysecure.net/hpf/1_1/?uID=" + uID;
            var url = _settingService.GetSettingByKey("apipaymentssettings.fromUrl").Value + uID;
            ViewBag.url = url;
            return View();
        }

        #endregion

    }
}
